feat: parallel upload & download image (#556)

* feat: parallel upload file

* feat: parallel download file
This commit is contained in:
My-Responsitories
2025-03-28 18:30:15 +08:00
committed by GitHub
parent 25995b0ed6
commit 76d031e8d1
7 changed files with 102 additions and 82 deletions

View File

@@ -88,7 +88,7 @@ class AccountManager extends Interceptor {
void onRequest(RequestOptions options, RequestInterceptorHandler handler) {
final path = options.path;
if (path.startsWith(GStorage.blockServer)) return handler.next(options);
if (_skipCookie(path)) return handler.next(options);
final Account account = options.extra['account'] ?? _findAccount(path);
@@ -148,8 +148,7 @@ class AccountManager extends Interceptor {
@override
void onResponse(Response response, ResponseInterceptorHandler handler) {
final path = response.requestOptions.path;
if (path.startsWith(HttpString.appBaseUrl) ||
path.startsWith(GStorage.blockServer)) {
if (path.startsWith(HttpString.appBaseUrl) || _skipCookie(path)) {
return handler.next(response);
} else {
_saveCookies(response).then((_) => handler.next(response)).catchError(
@@ -167,19 +166,25 @@ class AccountManager extends Interceptor {
@override
void onError(DioException err, ErrorInterceptorHandler handler) {
const List<String> skipShow = [
'heartbeat',
'history/report',
'roomEntryAction',
'seg.so',
'online/total',
'github',
'hdslb.com',
'biliimg.com'
];
String url = err.requestOptions.uri.toString();
debugPrint('🌹🌹ApiInterceptor: $url');
if (url.contains('heartbeat') ||
url.contains('history/report') ||
url.contains('roomEntryAction') ||
url.contains('seg.so') ||
url.contains('online/total') ||
url.contains('github') ||
if (skipShow.any((i) => url.contains(i)) ||
(url.contains('skipSegments') && err.requestOptions.method == 'GET')) {
// skip
} else {
dioError(err).then((res) => SmartDialog.showToast(res + url));
}
if (err.response != null &&
!err.response!.requestOptions.path.startsWith(HttpString.appBaseUrl)) {
_saveCookies(err.response!).then((_) => handler.next(err)).catchError(
@@ -231,6 +236,12 @@ class AccountManager extends Interceptor {
await account.onChange();
}
static bool _skipCookie(String path) {
return path.startsWith(GStorage.blockServer) ||
path.contains('hdslb.com') ||
path.contains('biliimg.com');
}
Account _findAccount(String path) => loginApi.contains(path)
? AnonymousAccount()
: Accounts.get(AccountType.values.firstWhere(

View File

@@ -2,6 +2,7 @@ import 'dart:typed_data';
import 'package:PiliPlus/http/init.dart';
import 'package:PiliPlus/utils/extension.dart';
import 'package:PiliPlus/utils/utils.dart';
import 'package:device_info_plus/device_info_plus.dart';
import 'package:dio/dio.dart';
import 'package:flutter/material.dart';
@@ -161,55 +162,59 @@ class DownloadUtils {
List<String> imgList, {
String imgType = 'cover',
}) async {
if (!await checkPermissionDependOnSdkInt(context)) return;
final cancelToken = CancelToken();
SmartDialog.showLoading(
msg: '正在下载原图',
clickMaskDismiss: true,
onDismiss: () {
cancelToken.cancel();
},
);
final picName =
"${imgType}_${DateTime.now().toString().substring(0, 19).replaceAll(' ', '_').replaceAll(':', '-')}";
try {
if (!await checkPermissionDependOnSdkInt(context)) {
return;
}
bool dispose = false;
for (int i = 0; i < imgList.length; i++) {
SmartDialog.showLoading(
msg:
'正在下载原图${imgList.length > 1 ? '${i + 1}/${imgList.length}' : ''}',
clickMaskDismiss: true,
onDismiss: () {
dispose = true;
if (i != imgList.length - 1) {
SmartDialog.showToast('已取消下载剩余图片');
}
},
);
await Future.wait(imgList.map((i) async {
var response = await Request().get(
imgList[i].http2https,
i.http2https,
options: Options(responseType: ResponseType.bytes),
cancelToken: cancelToken,
);
String picName =
"${imgType}_${DateTime.now().toString().replaceAll(' ', '_').replaceAll(':', '-').split('.').first}";
if (response.data is Map) {
throw HttpException(response.data['message']);
}
var name = '${picName}_${Utils.getFileName(i)}';
final SaveResult result = await SaverGallery.saveImage(
Uint8List.fromList(response.data),
response.data as Uint8List,
quality: 100,
fileName: picName,
fileName: name,
// 保存到 PiliPlus文件夹
androidRelativePath: "Pictures/PiliPlus",
skipIfExists: false,
);
// SmartDialog.dismiss();
// SmartDialog.showLoading(msg: '正在保存图片至图库');
if (i == imgList.length - 1) {
SmartDialog.dismiss();
}
if (result.isSuccess) {
SmartDialog.showToast('$picName」已保存 ');
// SmartDialog.showToast('「$name」已保存');
} else {
SmartDialog.showToast('保存失败,${result.errorMessage}');
throw Exception('保存失败,${result.errorMessage}');
}
if (dispose) {
break;
}
}
return true;
} catch (err) {
}), eagerError: true);
SmartDialog.dismiss();
SmartDialog.showToast(err.toString());
SmartDialog.showToast('图片已保存');
return true;
} catch (e) {
SmartDialog.dismiss();
if (cancelToken.isCancelled) {
SmartDialog.showToast('已取消下载');
} else {
SmartDialog.showToast(e.toString());
}
return false;
}
}

View File

@@ -1670,4 +1670,8 @@ class Utils {
List<int> randomBytes = generateRandomBytes(minLength, maxLength);
return base64.encode(randomBytes);
}
static String getFileName(String uri) {
return uri.substring(uri.lastIndexOf('/') + 1, uri.lastIndexOf('.'));
}
}

View File

@@ -3,6 +3,7 @@
// import md5 from 'md5'
// import axios from 'axios'
import 'dart:convert';
import 'package:PiliPlus/utils/utils.dart';
import 'package:crypto/crypto.dart';
import 'package:hive/hive.dart';
import 'package:synchronized/synchronized.dart';
@@ -66,10 +67,6 @@ class WbiSign {
md5.convert(utf8.encode(queryStr + mixinKey)).toString(); // 计算 w_rid
}
static String getFileName(String uri) {
return uri.substring(uri.lastIndexOf('/') + 1, uri.lastIndexOf('.'));
}
// 获取最新的 img_key 和 sub_key 可以从缓存中获取
static Future<String> getWbiKeys() async {
final DateTime nowDate = DateTime.now();
@@ -86,8 +83,8 @@ class WbiSign {
if (resp.data['code'] == 0) {
final wbiUrls = resp.data['data']['wbi_img'];
mixinKey = getMixinKey(
getFileName(wbiUrls['img_url']) + getFileName(wbiUrls['sub_url']));
mixinKey = getMixinKey(Utils.getFileName(wbiUrls['img_url']) +
Utils.getFileName(wbiUrls['sub_url']));
localCache.put(LocalCacheKey.mixinKey, mixinKey);
localCache.put(LocalCacheKey.timeStamp, nowDate.millisecondsSinceEpoch);