Commit 795acc7e5cb0789ab5aa00acd80f91c742bf7c44

Authored by Key
1 parent 37b78a15

feat: aliyun oss & avatar upload

lib/common/request/apis.dart
... ... @@ -70,4 +70,7 @@ class Apis {
70 70 /// 首页弹窗
71 71 /// get
72 72 static const String homePopup = 'home/popup';
  73 +
  74 + /// 获取阿里云oss鉴权信息
  75 + static const String aliyunOssSts = 'oss/sts/upload';
73 76 }
... ...
lib/common/request/basic_config.dart
1 1 class BasicConfig {
2 2 bool isTestDev = true;
3   -}
4 3 \ No newline at end of file
  4 +}
... ...
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&lt;T&gt; = 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&lt;T&gt;? convertListNotNull&lt;T&gt;(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&lt;T&gt;? convertListNotNull&lt;T&gt;(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&lt;T&gt;? convertListNotNull&lt;T&gt;(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&lt;String, dynamic&gt; 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&lt;String, dynamic&gt; $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 &#39;package:image_picker/image_picker.dart&#39;;
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&lt;UserAvatarEvent, UserAvatarState&gt; {
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&lt;UserAvatarEvent, UserAvatarState&gt; {
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&lt;UserAvatarEvent, UserAvatarState&gt; {
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&lt;UserAvatarEvent, UserAvatarState&gt; {
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
... ...