Commit 795acc7e5cb0789ab5aa00acd80f91c742bf7c44
1 parent
37b78a15
feat: aliyun oss & avatar upload
Showing
10 changed files
with
237 additions
and
22 deletions
lib/common/request/apis.dart
lib/common/request/basic_config.dart
lib/generated/json/aliyun_oss_upload_sts_entity.g.dart
0 → 100644
| 1 | +import 'package:wow_english/generated/json/base/json_convert_content.dart'; | |
| 2 | +import 'package:wow_english/models/aliyun_oss_upload_sts_entity.dart'; | |
| 3 | + | |
| 4 | +AliyunOssUploadStsEntity $AliyunOssUploadStsEntityFromJson(Map<String, dynamic> json) { | |
| 5 | + final AliyunOssUploadStsEntity aliyunOssUploadStsEntity = AliyunOssUploadStsEntity(); | |
| 6 | + final String? securityToken = jsonConvert.convert<String>(json['securityToken']); | |
| 7 | + if (securityToken != null) { | |
| 8 | + aliyunOssUploadStsEntity.securityToken = securityToken; | |
| 9 | + } | |
| 10 | + final String? expiration = jsonConvert.convert<String>(json['expiration']); | |
| 11 | + if (expiration != null) { | |
| 12 | + aliyunOssUploadStsEntity.expiration = expiration; | |
| 13 | + } | |
| 14 | + final String? endpoint = jsonConvert.convert<String>(json['endpoint']); | |
| 15 | + if (endpoint != null) { | |
| 16 | + aliyunOssUploadStsEntity.endpoint = endpoint; | |
| 17 | + } | |
| 18 | + final String? fileKey = jsonConvert.convert<String>(json['fileKey']); | |
| 19 | + if (fileKey != null) { | |
| 20 | + aliyunOssUploadStsEntity.fileKey = fileKey; | |
| 21 | + } | |
| 22 | + final String? accessKeyId = jsonConvert.convert<String>(json['accessKeyId']); | |
| 23 | + if (accessKeyId != null) { | |
| 24 | + aliyunOssUploadStsEntity.accessKeyId = accessKeyId; | |
| 25 | + } | |
| 26 | + final String? accessKeySecret = jsonConvert.convert<String>(json['accessKeySecret']); | |
| 27 | + if (accessKeySecret != null) { | |
| 28 | + aliyunOssUploadStsEntity.accessKeySecret = accessKeySecret; | |
| 29 | + } | |
| 30 | + final String? bucket = jsonConvert.convert<String>(json['bucket']); | |
| 31 | + if (bucket != null) { | |
| 32 | + aliyunOssUploadStsEntity.bucket = bucket; | |
| 33 | + } | |
| 34 | + final String? ossDomain = jsonConvert.convert<String>(json['ossDomain']); | |
| 35 | + if (ossDomain != null) { | |
| 36 | + aliyunOssUploadStsEntity.ossDomain = ossDomain; | |
| 37 | + } | |
| 38 | + final String? host = jsonConvert.convert<String>(json['host']); | |
| 39 | + if (host != null) { | |
| 40 | + aliyunOssUploadStsEntity.host = host; | |
| 41 | + } | |
| 42 | + final AliyunOssUploadStsCallbackParam? callbackParam = jsonConvert.convert<AliyunOssUploadStsCallbackParam>(json['callbackParam']); | |
| 43 | + if (callbackParam != null) { | |
| 44 | + aliyunOssUploadStsEntity.callbackParam = callbackParam; | |
| 45 | + } | |
| 46 | + return aliyunOssUploadStsEntity; | |
| 47 | +} | |
| 48 | + | |
| 49 | +Map<String, dynamic> $AliyunOssUploadStsEntityToJson(AliyunOssUploadStsEntity entity) { | |
| 50 | + final Map<String, dynamic> data = <String, dynamic>{}; | |
| 51 | + data['securityToken'] = entity.securityToken; | |
| 52 | + data['expiration'] = entity.expiration; | |
| 53 | + data['endpoint'] = entity.endpoint; | |
| 54 | + data['fileKey'] = entity.fileKey; | |
| 55 | + data['accessKeyId'] = entity.accessKeyId; | |
| 56 | + data['accessKeySecret'] = entity.accessKeySecret; | |
| 57 | + data['bucket'] = entity.bucket; | |
| 58 | + data['ossDomain'] = entity.ossDomain; | |
| 59 | + data['host'] = entity.host; | |
| 60 | + data['callbackParam'] = entity.callbackParam.toJson(); | |
| 61 | + return data; | |
| 62 | +} | |
| 63 | + | |
| 64 | +AliyunOssUploadStsCallbackParam $AliyunOssUploadStsCallbackParamFromJson(Map<String, dynamic> json) { | |
| 65 | + final AliyunOssUploadStsCallbackParam aliyunOssUploadStsCallbackParam = AliyunOssUploadStsCallbackParam(); | |
| 66 | + final String? callbackBody = jsonConvert.convert<String>(json['callbackBody']); | |
| 67 | + if (callbackBody != null) { | |
| 68 | + aliyunOssUploadStsCallbackParam.callbackBody = callbackBody; | |
| 69 | + } | |
| 70 | + final String? callbackBodyType = jsonConvert.convert<String>(json['callbackBodyType']); | |
| 71 | + if (callbackBodyType != null) { | |
| 72 | + aliyunOssUploadStsCallbackParam.callbackBodyType = callbackBodyType; | |
| 73 | + } | |
| 74 | + final String? callbackUrl = jsonConvert.convert<String>(json['callbackUrl']); | |
| 75 | + if (callbackUrl != null) { | |
| 76 | + aliyunOssUploadStsCallbackParam.callbackUrl = callbackUrl; | |
| 77 | + } | |
| 78 | + return aliyunOssUploadStsCallbackParam; | |
| 79 | +} | |
| 80 | + | |
| 81 | +Map<String, dynamic> $AliyunOssUploadStsCallbackParamToJson(AliyunOssUploadStsCallbackParam entity) { | |
| 82 | + final Map<String, dynamic> data = <String, dynamic>{}; | |
| 83 | + data['callbackBody'] = entity.callbackBody; | |
| 84 | + data['callbackBodyType'] = entity.callbackBodyType; | |
| 85 | + data['callbackUrl'] = entity.callbackUrl; | |
| 86 | + return data; | |
| 87 | +} | ... | ... |
lib/generated/json/base/json_convert_content.dart
| ... | ... | @@ -4,6 +4,7 @@ |
| 4 | 4 | |
| 5 | 5 | // This file is automatically generated. DO NOT EDIT, all your changes would be lost. |
| 6 | 6 | import 'package:flutter/material.dart' show debugPrint; |
| 7 | +import 'package:wow_english/models/aliyun_oss_upload_sts_entity.dart'; | |
| 7 | 8 | import 'package:wow_english/models/course_entity.dart'; |
| 8 | 9 | import 'package:wow_english/models/course_module_entity.dart'; |
| 9 | 10 | import 'package:wow_english/models/course_process_entity.dart'; |
| ... | ... | @@ -18,6 +19,8 @@ typedef EnumConvertFunction<T> = T Function(String value); |
| 18 | 19 | |
| 19 | 20 | class JsonConvert { |
| 20 | 21 | static final Map<String, JsonConvertFunction> convertFuncMap = { |
| 22 | + (AliyunOssUploadStsEntity).toString(): AliyunOssUploadStsEntity.fromJson, | |
| 23 | + (AliyunOssUploadStsCallbackParam).toString(): AliyunOssUploadStsCallbackParam.fromJson, | |
| 21 | 24 | (CourseEntity).toString(): CourseEntity.fromJson, |
| 22 | 25 | (CourseCourseLessons).toString(): CourseCourseLessons.fromJson, |
| 23 | 26 | (CourseModuleEntity).toString(): CourseModuleEntity.fromJson, |
| ... | ... | @@ -108,6 +111,12 @@ List<T>? convertListNotNull<T>(dynamic value, {EnumConvertFunction? enumConvert} |
| 108 | 111 | |
| 109 | 112 | //list is returned by type |
| 110 | 113 | static M? _getListChildType<M>(List<Map<String, dynamic>> data) { |
| 114 | + if(<AliyunOssUploadStsEntity>[] is M){ | |
| 115 | + return data.map<AliyunOssUploadStsEntity>((Map<String, dynamic> e) => AliyunOssUploadStsEntity.fromJson(e)).toList() as M; | |
| 116 | + } | |
| 117 | + if(<AliyunOssUploadStsCallbackParam>[] is M){ | |
| 118 | + return data.map<AliyunOssUploadStsCallbackParam>((Map<String, dynamic> e) => AliyunOssUploadStsCallbackParam.fromJson(e)).toList() as M; | |
| 119 | + } | |
| 111 | 120 | if(<CourseEntity>[] is M){ |
| 112 | 121 | return data.map<CourseEntity>((Map<String, dynamic> e) => CourseEntity.fromJson(e)).toList() as M; |
| 113 | 122 | } |
| ... | ... | @@ -146,7 +155,7 @@ List<T>? convertListNotNull<T>(dynamic value, {EnumConvertFunction? enumConvert} |
| 146 | 155 | } |
| 147 | 156 | |
| 148 | 157 | debugPrint("${M.toString()} not found"); |
| 149 | - | |
| 158 | + | |
| 150 | 159 | return null; |
| 151 | 160 | } |
| 152 | 161 | |
| ... | ... | @@ -157,4 +166,4 @@ List<T>? convertListNotNull<T>(dynamic value, {EnumConvertFunction? enumConvert} |
| 157 | 166 | return jsonConvert.convert<M>(json); |
| 158 | 167 | } |
| 159 | 168 | } |
| 160 | -} | |
| 161 | 169 | \ No newline at end of file |
| 170 | +} | ... | ... |
lib/generated/json/course_module_entity.g.dart
| ... | ... | @@ -59,7 +59,6 @@ CourseModuleEntity $CourseModuleEntityFromJson(Map<String, dynamic> json) { |
| 59 | 59 | if (status != null) { |
| 60 | 60 | courseModuleEntity.status = status; |
| 61 | 61 | } |
| 62 | - | |
| 63 | 62 | final String? courseModuleThemeColor = jsonConvert.convert<String>(json['courseModuleThemeColor']); |
| 64 | 63 | if (courseModuleThemeColor != null) { |
| 65 | 64 | courseModuleEntity.courseModuleThemeColor = courseModuleThemeColor; |
| ... | ... | @@ -85,4 +84,4 @@ Map<String, dynamic> $CourseModuleEntityToJson(CourseModuleEntity entity) { |
| 85 | 84 | data['status'] = entity.status; |
| 86 | 85 | data['courseModuleThemeColor'] = entity.courseModuleThemeColor; |
| 87 | 86 | return data; |
| 88 | -} | |
| 89 | 87 | \ No newline at end of file |
| 88 | +} | ... | ... |
lib/models/aliyun_oss_upload_sts_entity.dart
0 → 100644
| 1 | +import 'dart:convert'; | |
| 2 | + | |
| 3 | +import 'package:wow_english/generated/json/aliyun_oss_upload_sts_entity.g.dart'; | |
| 4 | +import 'package:wow_english/generated/json/base/json_field.dart'; | |
| 5 | + | |
| 6 | +@JsonSerializable() | |
| 7 | +class AliyunOssUploadStsEntity { | |
| 8 | + late String securityToken; | |
| 9 | + late String expiration; | |
| 10 | + late String endpoint; | |
| 11 | + late String fileKey; | |
| 12 | + late String accessKeyId; | |
| 13 | + late String accessKeySecret; | |
| 14 | + late String bucket; | |
| 15 | + late String ossDomain; | |
| 16 | + late String host; | |
| 17 | + late AliyunOssUploadStsCallbackParam callbackParam; | |
| 18 | + | |
| 19 | + AliyunOssUploadStsEntity(); | |
| 20 | + | |
| 21 | + factory AliyunOssUploadStsEntity.fromJson(Map<String, dynamic> json) => $AliyunOssUploadStsEntityFromJson(json); | |
| 22 | + | |
| 23 | + Map<String, dynamic> toJson() => $AliyunOssUploadStsEntityToJson(this); | |
| 24 | + | |
| 25 | + @override | |
| 26 | + String toString() { | |
| 27 | + return jsonEncode(this); | |
| 28 | + } | |
| 29 | +} | |
| 30 | + | |
| 31 | +@JsonSerializable() | |
| 32 | +class AliyunOssUploadStsCallbackParam { | |
| 33 | + late String callbackBody; | |
| 34 | + late String callbackBodyType; | |
| 35 | + late String callbackUrl; | |
| 36 | + | |
| 37 | + AliyunOssUploadStsCallbackParam(); | |
| 38 | + | |
| 39 | + factory AliyunOssUploadStsCallbackParam.fromJson(Map<String, dynamic> json) => $AliyunOssUploadStsCallbackParamFromJson(json); | |
| 40 | + | |
| 41 | + Map<String, dynamic> toJson() => $AliyunOssUploadStsCallbackParamToJson(this); | |
| 42 | + | |
| 43 | + @override | |
| 44 | + String toString() { | |
| 45 | + return jsonEncode(this); | |
| 46 | + } | |
| 47 | +} | ... | ... |
lib/pages/user/modify/user_avatar_bloc/user_avatar_bloc.dart
| ... | ... | @@ -4,13 +4,15 @@ import 'package:image_picker/image_picker.dart'; |
| 4 | 4 | import 'package:permission_handler/permission_handler.dart'; |
| 5 | 5 | import 'package:wow_english/common/core/assets_const.dart'; |
| 6 | 6 | import 'package:wow_english/common/core/user_util.dart'; |
| 7 | +import 'package:wow_english/common/request/dao/user_dao.dart'; | |
| 8 | +import 'package:wow_english/utils/aliyun_oss_util.dart'; | |
| 9 | +import 'package:wow_english/utils/log_util.dart'; | |
| 7 | 10 | |
| 8 | 11 | part 'user_avatar_event.dart'; |
| 9 | 12 | part 'user_avatar_state.dart'; |
| 10 | 13 | |
| 11 | 14 | class UserAvatarBloc extends Bloc<UserAvatarEvent, UserAvatarState> { |
| 12 | - | |
| 13 | - String _imageUrl = UserUtil.getUser()!.avatarUrl??AssetsConst.wowLogo; | |
| 15 | + String _imageUrl = UserUtil.getUser()!.avatarUrl ?? AssetsConst.wowLogo; | |
| 14 | 16 | |
| 15 | 17 | String get imageUrl => _imageUrl; |
| 16 | 18 | |
| ... | ... | @@ -18,27 +20,34 @@ class UserAvatarBloc extends Bloc<UserAvatarEvent, UserAvatarState> { |
| 18 | 20 | |
| 19 | 21 | XFile? get file => _file; |
| 20 | 22 | |
| 21 | - bool _canInsertApp =false; | |
| 23 | + bool _canInsertApp = false; | |
| 22 | 24 | |
| 23 | 25 | bool get canInsertApp => _canInsertApp; |
| 24 | 26 | |
| 25 | 27 | final ImagePicker picker = ImagePicker(); |
| 26 | 28 | |
| 27 | - | |
| 28 | 29 | UserAvatarBloc() : super(UserAvatarInitial()) { |
| 29 | 30 | on<ChangeImageEvent>(_changeImage); |
| 30 | 31 | on<GetImageFromPhotoEvent>(_getImageFromPhoto); |
| 31 | 32 | on<GetImageFromCameraEvent>(_getImageFromCamera); |
| 32 | 33 | } |
| 33 | 34 | |
| 34 | - void _changeImage(ChangeImageEvent event,Emitter<UserAvatarState> emitter) async { | |
| 35 | + void _changeImage(ChangeImageEvent event, Emitter<UserAvatarState> emitter) async { | |
| 35 | 36 | _imageUrl = event.imagePath; |
| 36 | - emitter(ChangeImageState()); | |
| 37 | + try { | |
| 38 | + // todo 加个loading UI | |
| 39 | + String avatarUrl = await AliyunOssUtil.uploadFile(event.imagePath); | |
| 40 | + // 上传服务器 | |
| 41 | + await UserDao.updateUserInfoField(avatarUrl: avatarUrl); | |
| 42 | + emitter(ChangeImageState()); | |
| 43 | + } catch (e) { | |
| 44 | + Log.e('上传头像失败:$e'); | |
| 45 | + } | |
| 37 | 46 | } |
| 38 | 47 | |
| 39 | - void _getImageFromPhoto(GetImageFromPhotoEvent event,Emitter<UserAvatarState> emitter) async { | |
| 48 | + void _getImageFromPhoto(GetImageFromPhotoEvent event, Emitter<UserAvatarState> emitter) async { | |
| 40 | 49 | await getPhotoPermissionStatus().then((value) async { |
| 41 | - if (!value){ | |
| 50 | + if (!value) { | |
| 42 | 51 | debugPrint('失败$value'); |
| 43 | 52 | return; |
| 44 | 53 | } |
| ... | ... | @@ -47,9 +56,9 @@ class UserAvatarBloc extends Bloc<UserAvatarEvent, UserAvatarState> { |
| 47 | 56 | }); |
| 48 | 57 | } |
| 49 | 58 | |
| 50 | - void _getImageFromCamera(GetImageFromCameraEvent event,Emitter<UserAvatarState> emitter) async { | |
| 59 | + void _getImageFromCamera(GetImageFromCameraEvent event, Emitter<UserAvatarState> emitter) async { | |
| 51 | 60 | await getCameraPermissionStatus().then((value) async { |
| 52 | - if (!value){ | |
| 61 | + if (!value) { | |
| 53 | 62 | debugPrint('失败$value'); |
| 54 | 63 | return; |
| 55 | 64 | } |
| ... | ... | @@ -71,9 +80,7 @@ class UserAvatarBloc extends Bloc<UserAvatarEvent, UserAvatarState> { |
| 71 | 80 | openAppSettings(); |
| 72 | 81 | } else if (status.isRestricted) { |
| 73 | 82 | _requestPermission(permission); |
| 74 | - } else { | |
| 75 | - | |
| 76 | - } | |
| 83 | + } else {} | |
| 77 | 84 | return false; |
| 78 | 85 | } |
| 79 | 86 | |
| ... | ... | @@ -89,9 +96,7 @@ class UserAvatarBloc extends Bloc<UserAvatarEvent, UserAvatarState> { |
| 89 | 96 | openAppSettings(); |
| 90 | 97 | } else if (status.isRestricted) { |
| 91 | 98 | _requestPermission(permission); |
| 92 | - } else { | |
| 93 | - | |
| 94 | - } | |
| 99 | + } else {} | |
| 95 | 100 | return false; |
| 96 | 101 | } |
| 97 | 102 | ... | ... |
lib/utils/aliyun_oss_util.dart
0 → 100644
| 1 | +import 'package:dio/dio.dart'; | |
| 2 | +import 'package:flutter_oss_aliyun/flutter_oss_aliyun.dart'; | |
| 3 | +import 'package:wow_english/common/request/request_client.dart'; | |
| 4 | +import 'package:wow_english/models/aliyun_oss_upload_sts_entity.dart'; | |
| 5 | +import 'package:wow_english/utils/log_util.dart'; | |
| 6 | + | |
| 7 | +/// 阿里云 oss 工具类,服务端给的鉴权一次有效 | |
| 8 | +/// 这个库的Client是个单例,如果并发使用请注意所调用的Client归属,Client.init会生成一个新的_instance | |
| 9 | +class AliyunOssUtil { | |
| 10 | + static Future<String> uploadFile(String filePath) async { | |
| 11 | + // 取出文件名 | |
| 12 | + String fileName = filePath.substring(filePath.lastIndexOf("/") + 1, filePath.length); | |
| 13 | + Log.d("待上传文件fileName: $fileName"); | |
| 14 | + // 获取鉴权信息 | |
| 15 | + AliyunOssUploadStsEntity stsEntity = await _getSts(fileName); | |
| 16 | + // 鉴权 | |
| 17 | + Client.init( | |
| 18 | + ossEndpoint: stsEntity.endpoint, | |
| 19 | + bucketName: stsEntity.bucket, | |
| 20 | + authGetter: stsEntity.authGetter, | |
| 21 | + ); | |
| 22 | + | |
| 23 | + // 上传文件 | |
| 24 | + final Response<dynamic> resp = await Client().putObjectFile( | |
| 25 | + filePath, | |
| 26 | + fileKey: stsEntity.fileKey, | |
| 27 | + option: PutRequestOption( | |
| 28 | + onSendProgress: (count, total) { | |
| 29 | + Log.d("send: count = $count, and total = $total"); | |
| 30 | + }, | |
| 31 | + onReceiveProgress: (count, total) { | |
| 32 | + Log.d("receive: count = $count, and total = $total"); | |
| 33 | + }, | |
| 34 | + aclModel: AclMode.private, | |
| 35 | + callback: Callback( | |
| 36 | + callbackUrl: stsEntity.callbackParam.callbackUrl, | |
| 37 | + callbackBody: stsEntity.callbackParam.callbackBody, | |
| 38 | + calbackBodyType: CalbackBodyType.json, | |
| 39 | + ), | |
| 40 | + ), | |
| 41 | + ); | |
| 42 | + return '${stsEntity.host}/${stsEntity.fileKey}'; | |
| 43 | + } | |
| 44 | + | |
| 45 | + /// 获取鉴权信息 | |
| 46 | + static Future<AliyunOssUploadStsEntity> _getSts(String fileName) async { | |
| 47 | + var result = await requestClient.get(Apis.aliyunOssSts, queryParameters: {'fileName': fileName}); | |
| 48 | + return result; | |
| 49 | + } | |
| 50 | +} | |
| 51 | + | |
| 52 | +extension StsExtension on AliyunOssUploadStsEntity { | |
| 53 | + Auth authGetter() { | |
| 54 | + return Auth( | |
| 55 | + accessKey: accessKeyId, | |
| 56 | + accessSecret: accessKeySecret, | |
| 57 | + expire: expiration, | |
| 58 | + secureToken: securityToken, | |
| 59 | + ); | |
| 60 | + } | |
| 61 | +} | ... | ... |
lib/utils/log_util.dart
| 1 | +import 'package:wow_english/common/request/basic_config.dart'; | |
| 2 | + | |
| 1 | 3 | enum LogLevel { debug, info, warning, error } |
| 2 | 4 | |
| 3 | 5 | class Log { |
| 4 | - static LogLevel level = LogLevel.debug; | |
| 6 | + static LogLevel level = BasicConfig().isTestDev ? LogLevel.debug : LogLevel.error; | |
| 5 | 7 | |
| 6 | 8 | /// debug |
| 7 | 9 | static void d(Object? object) { | ... | ... |
pubspec.yaml
| ... | ... | @@ -97,6 +97,8 @@ dependencies: |
| 97 | 97 | flutter_sound: ^9.2.13 |
| 98 | 98 | # 文件管理 https://pub.dev/packages/path_provider |
| 99 | 99 | path_provider: ^2.0.15 |
| 100 | + # 阿里云oss https://pub.dev/packages/flutter_oss_aliyun | |
| 101 | + flutter_oss_aliyun: ^6.2.7 | |
| 100 | 102 | |
| 101 | 103 | dev_dependencies: |
| 102 | 104 | build_runner: ^2.4.4 | ... | ... |