Skip to content

Commit dc284e2

Browse files
committed
feature:complete upyun image upload
1 parent ed6dbf3 commit dc284e2

File tree

3 files changed

+155
-17
lines changed

3 files changed

+155
-17
lines changed

lib/api/upyun_api.dart

Lines changed: 63 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -3,30 +3,87 @@ import 'dart:io';
33

44
import 'package:crypto/crypto.dart';
55
import 'package:dio/dio.dart';
6+
import 'package:flutter_picgo/utils/net.dart';
7+
import 'package:path/path.dart';
68

79
class UpyunApi {
810
static const BASE_URL = 'http://v0.api.upyun.com';
911

1012
static const String operatorKey = 'operatorKey';
1113
static const String passwordKey = 'passwordKey';
14+
15+
/// REST API PUT
16+
static Future putObject(
17+
File file,
18+
String operator,
19+
String password,
20+
String name,
21+
String bucket, {
22+
String path = '',
23+
}) async {
24+
String wholePath = joinAll([BASE_URL, bucket, path, name]);
25+
var bytes = file.readAsBytesSync();
26+
Response res = await NetUtils.getInstance().put(wholePath,
27+
data: Stream.fromIterable(bytes.map((e) => [e])),
28+
options: Options(
29+
headers: {
30+
Headers.contentLengthHeader: bytes.length,
31+
},
32+
contentType: 'image/${extension(name).replaceFirst('.', '')}',
33+
extra: {
34+
operatorKey: operator,
35+
passwordKey: password,
36+
}));
37+
return res.headers;
38+
}
39+
40+
/// REST API DELETE
41+
static Future deleteObject(
42+
String bucket,
43+
String operator,
44+
String password,
45+
String key,
46+
) async {
47+
String wholePath = joinAll([BASE_URL, bucket, key]);
48+
Response res = await NetUtils.getInstance().delete(wholePath,
49+
options: Options(
50+
extra: {
51+
operatorKey: operator,
52+
passwordKey: password,
53+
},
54+
));
55+
return res.headers;
56+
}
57+
58+
/// build policy
59+
static String buildPolicy(String bucket, String saveKey) {
60+
Map<String, dynamic> map = {
61+
'bucket': bucket,
62+
'save-key': saveKey,
63+
'expiration': DateTime.now().millisecondsSinceEpoch + 30 * 60 * 1000,
64+
};
65+
return base64.encode(utf8.encode(json.encode(map)));
66+
}
1267
}
1368

1469
class UpyunInterceptor extends InterceptorsWrapper {
15-
1670
@override
1771
Future onRequest(RequestOptions options) async {
18-
if (options.path.contains(UpyunApi.BASE_URL)) {
72+
if (options.path.contains(UpyunApi.BASE_URL.replaceFirst('http://', ''))) {
1973
/// 请求方式,如:GET、POST、PUT、HEAD 等
20-
String method = options.method.toLowerCase();
74+
String method = options.method.toUpperCase();
2175
String path = options.uri.path;
2276
String date = HttpDate.format(DateTime.now());
23-
String pwdMd5 = '${md5.convert(options.extra[UpyunApi.passwordKey])}';
77+
String pwdMd5 =
78+
'${md5.convert(utf8.encode(options.extra[UpyunApi.passwordKey]))}';
2479
String operator = options.extra[UpyunApi.operatorKey];
80+
String sign = '$method&$path&$date';
2581

2682
/// 签名构造
27-
var hmacsha1 = Hmac(sha1, utf8.encode(pwdMd5));
28-
var auth = hmacsha1.convert(utf8.encode('$method&$path&$date'));
83+
var hmacsha1 = Hmac(sha1, utf8.encode('$pwdMd5'));
84+
var auth = hmacsha1.convert(utf8.encode(sign));
2985
String realAuth = base64.encode(auth.bytes);
86+
3087
/// Add Common Header
3188
options.headers.addAll({
3289
'Date': date,

lib/utils/net.dart

Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
import 'package:dio/dio.dart';
22
import 'package:flutter_picgo/api/tcyun_api.dart';
3+
import 'package:flutter_picgo/api/upyun_api.dart';
34

45
const bool inProduction = const bool.fromEnvironment("dart.vm.product");
56

@@ -10,14 +11,19 @@ class NetUtils {
1011
NetUtils._internal() {
1112
_dio = new Dio(BaseOptions(
1213
connectTimeout: 30000, receiveTimeout: 30000, sendTimeout: 30000));
14+
15+
/// Tcyun Interceptor
16+
dio.interceptors.add(TcyunInterceptor());
17+
18+
/// Upyun Interceptor
19+
dio.interceptors.add(UpyunInterceptor());
20+
21+
/// Log Interceptor
1322
if (!inProduction) {
1423
/// Log
1524
dio.interceptors
1625
.add(LogInterceptor(requestBody: true, responseBody: true));
1726
}
18-
19-
/// Tcyun Interceptor
20-
dio.interceptors.add(TcyunInterceptor());
2127
}
2228

2329
Dio get dio => _dio;
Lines changed: 83 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,19 +1,94 @@
1+
import 'dart:convert';
2+
3+
import 'package:flutter_picgo/api/upyun_api.dart';
14
import 'package:flutter_picgo/model/uploaded.dart';
5+
import 'package:flutter_picgo/model/upyun_config.dart';
6+
import 'package:flutter_picgo/resources/pb_type_keys.dart';
7+
import 'package:flutter_picgo/utils/image_upload.dart';
28
import 'dart:io';
3-
9+
import 'package:path/path.dart';
410
import 'package:flutter_picgo/utils/strategy/image_upload_strategy.dart';
11+
import 'package:flutter_picgo/utils/strings.dart';
512

613
class UpyunImageUpload implements ImageUploadStrategy {
714
@override
8-
Future<Uploaded> delete(Uploaded uploaded) {
9-
// TODO: implement delete
10-
throw UnimplementedError();
15+
Future<Uploaded> delete(Uploaded uploaded) async {
16+
UpyunUploadedInfo info;
17+
try {
18+
info = UpyunUploadedInfo.fromJson(json.decode(uploaded.info));
19+
} catch (e) {}
20+
if (info != null) {
21+
await UpyunApi.deleteObject(
22+
info.bucket, info.operator, info.password, info.key);
23+
}
24+
return uploaded;
25+
}
26+
27+
@override
28+
Future<Uploaded> upload(File file, String renameImage) async {
29+
String configStr = await ImageUploadUtils.getPBConfig(PBTypeKeys.upyun);
30+
if (isBlank(configStr)) {
31+
throw UpyunError(error: '读取配置文件错误!请重试');
1132
}
33+
UpyunConfig config = UpyunConfig.fromJson(json.decode(configStr));
34+
await UpyunApi.putObject(
35+
file, config.operator, config.password, renameImage, config.bucket,
36+
path: config.path);
37+
String wholeKey = joinAll([config.path ?? '', renameImage]);
38+
String imagePath =
39+
joinAll([config.url, '$wholeKey${config.options ?? ''}']);
40+
var uploadedItem = Uploaded(-1, '$imagePath', PBTypeKeys.upyun,
41+
info: json.encode(UpyunUploadedInfo(
42+
operator: config.operator,
43+
password: config.password,
44+
bucket: config.bucket,
45+
key: wholeKey,
46+
)));
47+
await ImageUploadUtils.saveUploadedItem(uploadedItem);
48+
return uploadedItem;
49+
}
50+
}
51+
52+
class UpyunError implements Exception {
53+
UpyunError({
54+
this.error,
55+
});
56+
57+
dynamic error;
58+
59+
String get message => (error?.toString() ?? '');
1260

1361
@override
14-
Future<Uploaded> upload(File file, String renameImage) {
15-
// TODO: implement upload
16-
throw UnimplementedError();
62+
String toString() {
63+
var msg = 'UpyunError $message';
64+
if (error is Error) {
65+
msg += '\n${error.stackTrace}';
66+
}
67+
return msg;
68+
}
69+
}
70+
71+
class UpyunUploadedInfo {
72+
String operator;
73+
String bucket;
74+
String password;
75+
String key;
76+
77+
UpyunUploadedInfo({this.operator, this.bucket, this.password, this.key});
78+
79+
UpyunUploadedInfo.fromJson(Map<String, dynamic> json) {
80+
operator = json['operator'];
81+
bucket = json['bucket'];
82+
password = json['password'];
83+
key = json['key'];
1784
}
1885

19-
}
86+
Map<String, dynamic> toJson() {
87+
final Map<String, dynamic> data = new Map<String, dynamic>();
88+
data['operator'] = this.operator;
89+
data['bucket'] = this.bucket;
90+
data['password'] = this.password;
91+
data['key'] = this.key;
92+
return data;
93+
}
94+
}

0 commit comments

Comments
 (0)