diff --git a/lib/common/request/apis.dart b/lib/common/request/apis.dart index 9fb2f43..9d73ee8 100644 --- a/lib/common/request/apis.dart +++ b/lib/common/request/apis.dart @@ -70,4 +70,7 @@ class Apis { /// 首页弹窗 /// get static const String homePopup = 'home/popup'; + + /// 获取阿里云oss鉴权信息 + static const String aliyunOssSts = 'oss/sts/upload'; } diff --git a/lib/common/request/basic_config.dart b/lib/common/request/basic_config.dart index 57a798c..f86b8f9 100644 --- a/lib/common/request/basic_config.dart +++ b/lib/common/request/basic_config.dart @@ -1,3 +1,3 @@ class BasicConfig { bool isTestDev = true; -} \ No newline at end of file +} diff --git a/lib/generated/json/aliyun_oss_upload_sts_entity.g.dart b/lib/generated/json/aliyun_oss_upload_sts_entity.g.dart new file mode 100644 index 0000000..2dfdcca --- /dev/null +++ b/lib/generated/json/aliyun_oss_upload_sts_entity.g.dart @@ -0,0 +1,87 @@ +import 'package:wow_english/generated/json/base/json_convert_content.dart'; +import 'package:wow_english/models/aliyun_oss_upload_sts_entity.dart'; + +AliyunOssUploadStsEntity $AliyunOssUploadStsEntityFromJson(Map json) { + final AliyunOssUploadStsEntity aliyunOssUploadStsEntity = AliyunOssUploadStsEntity(); + final String? securityToken = jsonConvert.convert(json['securityToken']); + if (securityToken != null) { + aliyunOssUploadStsEntity.securityToken = securityToken; + } + final String? expiration = jsonConvert.convert(json['expiration']); + if (expiration != null) { + aliyunOssUploadStsEntity.expiration = expiration; + } + final String? endpoint = jsonConvert.convert(json['endpoint']); + if (endpoint != null) { + aliyunOssUploadStsEntity.endpoint = endpoint; + } + final String? fileKey = jsonConvert.convert(json['fileKey']); + if (fileKey != null) { + aliyunOssUploadStsEntity.fileKey = fileKey; + } + final String? accessKeyId = jsonConvert.convert(json['accessKeyId']); + if (accessKeyId != null) { + aliyunOssUploadStsEntity.accessKeyId = accessKeyId; + } + final String? accessKeySecret = jsonConvert.convert(json['accessKeySecret']); + if (accessKeySecret != null) { + aliyunOssUploadStsEntity.accessKeySecret = accessKeySecret; + } + final String? bucket = jsonConvert.convert(json['bucket']); + if (bucket != null) { + aliyunOssUploadStsEntity.bucket = bucket; + } + final String? ossDomain = jsonConvert.convert(json['ossDomain']); + if (ossDomain != null) { + aliyunOssUploadStsEntity.ossDomain = ossDomain; + } + final String? host = jsonConvert.convert(json['host']); + if (host != null) { + aliyunOssUploadStsEntity.host = host; + } + final AliyunOssUploadStsCallbackParam? callbackParam = jsonConvert.convert(json['callbackParam']); + if (callbackParam != null) { + aliyunOssUploadStsEntity.callbackParam = callbackParam; + } + return aliyunOssUploadStsEntity; +} + +Map $AliyunOssUploadStsEntityToJson(AliyunOssUploadStsEntity entity) { + final Map data = {}; + data['securityToken'] = entity.securityToken; + data['expiration'] = entity.expiration; + data['endpoint'] = entity.endpoint; + data['fileKey'] = entity.fileKey; + data['accessKeyId'] = entity.accessKeyId; + data['accessKeySecret'] = entity.accessKeySecret; + data['bucket'] = entity.bucket; + data['ossDomain'] = entity.ossDomain; + data['host'] = entity.host; + data['callbackParam'] = entity.callbackParam.toJson(); + return data; +} + +AliyunOssUploadStsCallbackParam $AliyunOssUploadStsCallbackParamFromJson(Map json) { + final AliyunOssUploadStsCallbackParam aliyunOssUploadStsCallbackParam = AliyunOssUploadStsCallbackParam(); + final String? callbackBody = jsonConvert.convert(json['callbackBody']); + if (callbackBody != null) { + aliyunOssUploadStsCallbackParam.callbackBody = callbackBody; + } + final String? callbackBodyType = jsonConvert.convert(json['callbackBodyType']); + if (callbackBodyType != null) { + aliyunOssUploadStsCallbackParam.callbackBodyType = callbackBodyType; + } + final String? callbackUrl = jsonConvert.convert(json['callbackUrl']); + if (callbackUrl != null) { + aliyunOssUploadStsCallbackParam.callbackUrl = callbackUrl; + } + return aliyunOssUploadStsCallbackParam; +} + +Map $AliyunOssUploadStsCallbackParamToJson(AliyunOssUploadStsCallbackParam entity) { + final Map data = {}; + data['callbackBody'] = entity.callbackBody; + data['callbackBodyType'] = entity.callbackBodyType; + data['callbackUrl'] = entity.callbackUrl; + return data; +} diff --git a/lib/generated/json/base/json_convert_content.dart b/lib/generated/json/base/json_convert_content.dart index 6821f57..2b24e4c 100644 --- a/lib/generated/json/base/json_convert_content.dart +++ b/lib/generated/json/base/json_convert_content.dart @@ -4,6 +4,7 @@ // This file is automatically generated. DO NOT EDIT, all your changes would be lost. import 'package:flutter/material.dart' show debugPrint; +import 'package:wow_english/models/aliyun_oss_upload_sts_entity.dart'; import 'package:wow_english/models/course_entity.dart'; import 'package:wow_english/models/course_module_entity.dart'; import 'package:wow_english/models/course_process_entity.dart'; @@ -18,6 +19,8 @@ typedef EnumConvertFunction = T Function(String value); class JsonConvert { static final Map convertFuncMap = { + (AliyunOssUploadStsEntity).toString(): AliyunOssUploadStsEntity.fromJson, + (AliyunOssUploadStsCallbackParam).toString(): AliyunOssUploadStsCallbackParam.fromJson, (CourseEntity).toString(): CourseEntity.fromJson, (CourseCourseLessons).toString(): CourseCourseLessons.fromJson, (CourseModuleEntity).toString(): CourseModuleEntity.fromJson, @@ -108,6 +111,12 @@ List? convertListNotNull(dynamic value, {EnumConvertFunction? enumConvert} //list is returned by type static M? _getListChildType(List> data) { + if([] is M){ + return data.map((Map e) => AliyunOssUploadStsEntity.fromJson(e)).toList() as M; + } + if([] is M){ + return data.map((Map e) => AliyunOssUploadStsCallbackParam.fromJson(e)).toList() as M; + } if([] is M){ return data.map((Map e) => CourseEntity.fromJson(e)).toList() as M; } @@ -146,7 +155,7 @@ List? convertListNotNull(dynamic value, {EnumConvertFunction? enumConvert} } debugPrint("${M.toString()} not found"); - + return null; } @@ -157,4 +166,4 @@ List? convertListNotNull(dynamic value, {EnumConvertFunction? enumConvert} return jsonConvert.convert(json); } } -} \ No newline at end of file +} diff --git a/lib/generated/json/course_module_entity.g.dart b/lib/generated/json/course_module_entity.g.dart index 1d091a6..1e9182d 100644 --- a/lib/generated/json/course_module_entity.g.dart +++ b/lib/generated/json/course_module_entity.g.dart @@ -59,7 +59,6 @@ CourseModuleEntity $CourseModuleEntityFromJson(Map json) { if (status != null) { courseModuleEntity.status = status; } - final String? courseModuleThemeColor = jsonConvert.convert(json['courseModuleThemeColor']); if (courseModuleThemeColor != null) { courseModuleEntity.courseModuleThemeColor = courseModuleThemeColor; @@ -85,4 +84,4 @@ Map $CourseModuleEntityToJson(CourseModuleEntity entity) { data['status'] = entity.status; data['courseModuleThemeColor'] = entity.courseModuleThemeColor; return data; -} \ No newline at end of file +} diff --git a/lib/models/aliyun_oss_upload_sts_entity.dart b/lib/models/aliyun_oss_upload_sts_entity.dart new file mode 100644 index 0000000..bd66f95 --- /dev/null +++ b/lib/models/aliyun_oss_upload_sts_entity.dart @@ -0,0 +1,47 @@ +import 'dart:convert'; + +import 'package:wow_english/generated/json/aliyun_oss_upload_sts_entity.g.dart'; +import 'package:wow_english/generated/json/base/json_field.dart'; + +@JsonSerializable() +class AliyunOssUploadStsEntity { + late String securityToken; + late String expiration; + late String endpoint; + late String fileKey; + late String accessKeyId; + late String accessKeySecret; + late String bucket; + late String ossDomain; + late String host; + late AliyunOssUploadStsCallbackParam callbackParam; + + AliyunOssUploadStsEntity(); + + factory AliyunOssUploadStsEntity.fromJson(Map json) => $AliyunOssUploadStsEntityFromJson(json); + + Map toJson() => $AliyunOssUploadStsEntityToJson(this); + + @override + String toString() { + return jsonEncode(this); + } +} + +@JsonSerializable() +class AliyunOssUploadStsCallbackParam { + late String callbackBody; + late String callbackBodyType; + late String callbackUrl; + + AliyunOssUploadStsCallbackParam(); + + factory AliyunOssUploadStsCallbackParam.fromJson(Map json) => $AliyunOssUploadStsCallbackParamFromJson(json); + + Map toJson() => $AliyunOssUploadStsCallbackParamToJson(this); + + @override + String toString() { + return jsonEncode(this); + } +} diff --git a/lib/pages/user/modify/user_avatar_bloc/user_avatar_bloc.dart b/lib/pages/user/modify/user_avatar_bloc/user_avatar_bloc.dart index 2bf6de8..59262a1 100644 --- a/lib/pages/user/modify/user_avatar_bloc/user_avatar_bloc.dart +++ b/lib/pages/user/modify/user_avatar_bloc/user_avatar_bloc.dart @@ -4,13 +4,15 @@ import 'package:image_picker/image_picker.dart'; import 'package:permission_handler/permission_handler.dart'; import 'package:wow_english/common/core/assets_const.dart'; import 'package:wow_english/common/core/user_util.dart'; +import 'package:wow_english/common/request/dao/user_dao.dart'; +import 'package:wow_english/utils/aliyun_oss_util.dart'; +import 'package:wow_english/utils/log_util.dart'; part 'user_avatar_event.dart'; part 'user_avatar_state.dart'; class UserAvatarBloc extends Bloc { - - String _imageUrl = UserUtil.getUser()!.avatarUrl??AssetsConst.wowLogo; + String _imageUrl = UserUtil.getUser()!.avatarUrl ?? AssetsConst.wowLogo; String get imageUrl => _imageUrl; @@ -18,27 +20,34 @@ class UserAvatarBloc extends Bloc { XFile? get file => _file; - bool _canInsertApp =false; + bool _canInsertApp = false; bool get canInsertApp => _canInsertApp; final ImagePicker picker = ImagePicker(); - UserAvatarBloc() : super(UserAvatarInitial()) { on(_changeImage); on(_getImageFromPhoto); on(_getImageFromCamera); } - void _changeImage(ChangeImageEvent event,Emitter emitter) async { + void _changeImage(ChangeImageEvent event, Emitter emitter) async { _imageUrl = event.imagePath; - emitter(ChangeImageState()); + try { + // todo 加个loading UI + String avatarUrl = await AliyunOssUtil.uploadFile(event.imagePath); + // 上传服务器 + await UserDao.updateUserInfoField(avatarUrl: avatarUrl); + emitter(ChangeImageState()); + } catch (e) { + Log.e('上传头像失败:$e'); + } } - void _getImageFromPhoto(GetImageFromPhotoEvent event,Emitter emitter) async { + void _getImageFromPhoto(GetImageFromPhotoEvent event, Emitter emitter) async { await getPhotoPermissionStatus().then((value) async { - if (!value){ + if (!value) { debugPrint('失败$value'); return; } @@ -47,9 +56,9 @@ class UserAvatarBloc extends Bloc { }); } - void _getImageFromCamera(GetImageFromCameraEvent event,Emitter emitter) async { + void _getImageFromCamera(GetImageFromCameraEvent event, Emitter emitter) async { await getCameraPermissionStatus().then((value) async { - if (!value){ + if (!value) { debugPrint('失败$value'); return; } @@ -71,9 +80,7 @@ class UserAvatarBloc extends Bloc { openAppSettings(); } else if (status.isRestricted) { _requestPermission(permission); - } else { - - } + } else {} return false; } @@ -89,9 +96,7 @@ class UserAvatarBloc extends Bloc { openAppSettings(); } else if (status.isRestricted) { _requestPermission(permission); - } else { - - } + } else {} return false; } diff --git a/lib/utils/aliyun_oss_util.dart b/lib/utils/aliyun_oss_util.dart new file mode 100644 index 0000000..c590789 --- /dev/null +++ b/lib/utils/aliyun_oss_util.dart @@ -0,0 +1,61 @@ +import 'package:dio/dio.dart'; +import 'package:flutter_oss_aliyun/flutter_oss_aliyun.dart'; +import 'package:wow_english/common/request/request_client.dart'; +import 'package:wow_english/models/aliyun_oss_upload_sts_entity.dart'; +import 'package:wow_english/utils/log_util.dart'; + +/// 阿里云 oss 工具类,服务端给的鉴权一次有效 +/// 这个库的Client是个单例,如果并发使用请注意所调用的Client归属,Client.init会生成一个新的_instance +class AliyunOssUtil { + static Future uploadFile(String filePath) async { + // 取出文件名 + String fileName = filePath.substring(filePath.lastIndexOf("/") + 1, filePath.length); + Log.d("待上传文件fileName: $fileName"); + // 获取鉴权信息 + AliyunOssUploadStsEntity stsEntity = await _getSts(fileName); + // 鉴权 + Client.init( + ossEndpoint: stsEntity.endpoint, + bucketName: stsEntity.bucket, + authGetter: stsEntity.authGetter, + ); + + // 上传文件 + final Response resp = await Client().putObjectFile( + filePath, + fileKey: stsEntity.fileKey, + option: PutRequestOption( + onSendProgress: (count, total) { + Log.d("send: count = $count, and total = $total"); + }, + onReceiveProgress: (count, total) { + Log.d("receive: count = $count, and total = $total"); + }, + aclModel: AclMode.private, + callback: Callback( + callbackUrl: stsEntity.callbackParam.callbackUrl, + callbackBody: stsEntity.callbackParam.callbackBody, + calbackBodyType: CalbackBodyType.json, + ), + ), + ); + return '${stsEntity.host}/${stsEntity.fileKey}'; + } + + /// 获取鉴权信息 + static Future _getSts(String fileName) async { + var result = await requestClient.get(Apis.aliyunOssSts, queryParameters: {'fileName': fileName}); + return result; + } +} + +extension StsExtension on AliyunOssUploadStsEntity { + Auth authGetter() { + return Auth( + accessKey: accessKeyId, + accessSecret: accessKeySecret, + expire: expiration, + secureToken: securityToken, + ); + } +} diff --git a/lib/utils/log_util.dart b/lib/utils/log_util.dart index 51b5037..4b50c94 100644 --- a/lib/utils/log_util.dart +++ b/lib/utils/log_util.dart @@ -1,7 +1,9 @@ +import 'package:wow_english/common/request/basic_config.dart'; + enum LogLevel { debug, info, warning, error } class Log { - static LogLevel level = LogLevel.debug; + static LogLevel level = BasicConfig().isTestDev ? LogLevel.debug : LogLevel.error; /// debug static void d(Object? object) { diff --git a/pubspec.yaml b/pubspec.yaml index 9b1fdb1..dc98d45 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -97,6 +97,8 @@ dependencies: flutter_sound: ^9.2.13 # 文件管理 https://pub.dev/packages/path_provider path_provider: ^2.0.15 + # 阿里云oss https://pub.dev/packages/flutter_oss_aliyun + flutter_oss_aliyun: ^6.2.7 dev_dependencies: build_runner: ^2.4.4