diff --git a/lib/http/msg.dart b/lib/http/msg.dart index 7e410646b..7ef58a684 100644 --- a/lib/http/msg.dart +++ b/lib/http/msg.dart @@ -231,22 +231,24 @@ class MsgHttp { dynamic path, String? category, String? biz, + CancelToken? cancelToken, }) async { - String csrf = await Request.getCsrf(); - Map data = await WbiSign.makSign({ - 'file_up': await MultipartFile.fromFile(path), + final file = await MultipartFile.fromFile(path); + Map data = { + 'file_up': file, if (category != null) 'category': category, if (biz != null) 'biz': biz, - 'csrf': csrf, - }); + 'csrf': await Request.getCsrf(), + }; var res = await Request().post( Api.uploadBfs, data: FormData.fromMap(data), + cancelToken: cancelToken, ); if (res.data['code'] == 0) { return { 'status': true, - 'data': res.data['data'], + 'data': res.data['data']..['img_size'] = file.length, }; } else { return { diff --git a/lib/pages/common/common_publish_page.dart b/lib/pages/common/common_publish_page.dart index 975af0d84..e60b3a433 100644 --- a/lib/pages/common/common_publish_page.dart +++ b/lib/pages/common/common_publish_page.dart @@ -9,6 +9,7 @@ import 'package:PiliPlus/common/widgets/interactiveviewer_gallery/interactivevie import 'package:PiliPlus/http/msg.dart'; import 'package:PiliPlus/utils/extension.dart'; import 'package:chat_bottom_container/chat_bottom_container.dart'; +import 'package:dio/dio.dart'; import 'package:easy_debounce/easy_throttle.dart'; import 'package:flutter/material.dart'; import 'package:flutter_smart_dialog/flutter_smart_dialog.dart'; @@ -161,32 +162,34 @@ abstract class CommonPublishPageState Future onPublish() async { feedBack(); - List? pictures; + List>? pictures; if (pathList.isNotEmpty) { - pictures = []; - for (int i = 0; i < pathList.length; i++) { - SmartDialog.showLoading(msg: '正在上传图片: ${i + 1}/${pathList.length}'); - dynamic result = await MsgHttp.uploadBfs( - path: pathList[i], - category: 'daily', - biz: 'new_dyn', - ); - if (result['status']) { - int imageSize = await File(pathList[i]).length(); - pictures.add({ - 'img_width': result['data']['image_width'], - 'img_height': result['data']['image_height'], - 'img_size': imageSize / 1024, - 'img_src': result['data']['image_url'], - }); - } else { - SmartDialog.dismiss(); - SmartDialog.showToast(result['msg']); - return; - } - if (i == pathList.length - 1) { - SmartDialog.dismiss(); - } + SmartDialog.showLoading(msg: '正在上传图片...'); + final cancelToken = CancelToken(); + try { + pictures = await Future.wait>( + pathList.map((path) async { + Map result = await MsgHttp.uploadBfs( + path: path, + category: 'daily', + biz: 'new_dyn', + cancelToken: cancelToken, + ); + if (!result['status']) throw HttpException(result['msg']); + return { + 'img_width': result['data']['image_width'], + 'img_height': result['data']['image_height'], + 'img_size': result['data']['img_size'] / 1024, + 'img_src': result['data']['image_url'], + }; + }).toList(), + eagerError: true); + SmartDialog.dismiss(); + } on HttpException catch (e) { + cancelToken.cancel(); + SmartDialog.dismiss(); + SmartDialog.showToast(e.message); + return; } } onCustomPublish(message: editController.text, pictures: pictures); diff --git a/lib/pages/whisper_detail/view.dart b/lib/pages/whisper_detail/view.dart index 0d8b85625..c5c87726c 100644 --- a/lib/pages/whisper_detail/view.dart +++ b/lib/pages/whisper_detail/view.dart @@ -1,5 +1,4 @@ import 'dart:async'; -import 'dart:io'; import 'package:PiliPlus/common/widgets/refresh_indicator.dart'; import 'package:PiliPlus/http/msg.dart'; import 'package:PiliPlus/models/msg/session.dart'; @@ -273,7 +272,6 @@ class _WhisperDetailPageState biz: 'im', ); if (result['status']) { - int imageSize = await File(pickedFile.path).length(); String mimeType = lookupMimeType(pickedFile.path) ?.split('/') .getOrNull(1) ?? @@ -284,7 +282,7 @@ class _WhisperDetailPageState 'width': result['data']['image_width'], 'imageType': mimeType, 'original': 1, - 'size': imageSize / 1024, + 'size': result['data']['img_size'] / 1024, }; SmartDialog.showLoading(msg: '正在发送'); await _whisperDetailController.sendMsg( diff --git a/lib/utils/accounts/account_manager/account_mgr.dart b/lib/utils/accounts/account_manager/account_mgr.dart index c78cd04ce..6d233dde2 100644 --- a/lib/utils/accounts/account_manager/account_mgr.dart +++ b/lib/utils/accounts/account_manager/account_mgr.dart @@ -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 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( diff --git a/lib/utils/download.dart b/lib/utils/download.dart index ac324c900..0b0502ba5 100644 --- a/lib/utils/download.dart +++ b/lib/utils/download.dart @@ -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 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; } } diff --git a/lib/utils/utils.dart b/lib/utils/utils.dart index 9b83dc697..2ee43d4e0 100644 --- a/lib/utils/utils.dart +++ b/lib/utils/utils.dart @@ -1670,4 +1670,8 @@ class Utils { List randomBytes = generateRandomBytes(minLength, maxLength); return base64.encode(randomBytes); } + + static String getFileName(String uri) { + return uri.substring(uri.lastIndexOf('/') + 1, uri.lastIndexOf('.')); + } } diff --git a/lib/utils/wbi_sign.dart b/lib/utils/wbi_sign.dart index 65dc3d003..12ad3c7c5 100644 --- a/lib/utils/wbi_sign.dart +++ b/lib/utils/wbi_sign.dart @@ -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 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);