Skip to content

Commit a38706b

Browse files
committed
v1.3.3+220 feat(下载工具): 实现下载取消功能并更新版本号
添加下载取消功能,通过维护CancelToken列表实现批量取消 更新应用版本号至1.3.1+220 优化代码格式和错误处理逻辑
1 parent 8ed44f0 commit a38706b

File tree

3 files changed

+81
-51
lines changed

3 files changed

+81
-51
lines changed

lib/oss_licenses.dart

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -11611,12 +11611,12 @@ THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
1161111611
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.''',
1161211612
);
1161311613

11614-
/// cyanitalk 1.3.1+210
11614+
/// cyanitalk 1.3.1+220
1161511615
const _cyanitalk = Package(
1161611616
name: 'cyanitalk',
1161711617
description: 'A comprehensive, multi-functional social application built with Flutter, connecting Misskey and Flarum, and belonging to CyaniAgent.',
1161811618
authors: [],
11619-
version: '1.3.1+210',
11619+
version: '1.3.1+220',
1162011620
spdxIdentifiers: ['MIT'],
1162111621
isMarkdown: false,
1162211622
isSdk: false,

lib/src/core/utils/download_utils.dart

Lines changed: 78 additions & 48 deletions
Original file line numberDiff line numberDiff line change
@@ -14,10 +14,12 @@ enum DownloadStatus {
1414
}
1515

1616
/// 下载进度回调
17-
typedef DownloadProgressCallback = void Function(int received, int total, double progress);
17+
typedef DownloadProgressCallback =
18+
void Function(int received, int total, double progress);
1819

1920
/// 下载状态回调
20-
typedef DownloadStatusCallback = void Function(DownloadStatus status, String? message);
21+
typedef DownloadStatusCallback =
22+
void Function(DownloadStatus status, String? message);
2123

2224
/// 下载配置类
2325
class DownloadConfig {
@@ -96,53 +98,64 @@ class DownloadUtils {
9698
),
9799
);
98100

101+
/// 存储所有下载的CancelToken
102+
static final List<CancelToken> _cancelTokens = [];
103+
99104
/// 单文件下载
100105
static Future<DownloadResult> downloadFile({
101106
required DownloadConfig config,
102107
DownloadProgressCallback? onProgress,
103108
DownloadStatusCallback? onStatusChange,
104109
}) async {
105-
try {
106-
// 通知开始下载
107-
onStatusChange?.call(DownloadStatus.pending, 'download_preparing'.tr());
110+
// 通知开始下载
111+
onStatusChange?.call(DownloadStatus.pending, 'download_preparing'.tr());
108112

109-
// 确定保存目录
110-
final String saveDirectory = config.saveDir ?? await _getDefaultDownloadDir();
111-
if (saveDirectory.isEmpty) {
112-
return DownloadResult(
113-
status: DownloadStatus.failed,
114-
errorMessage: 'download_no_directory'.tr(),
115-
);
116-
}
113+
// 确定保存目录
114+
final String saveDirectory =
115+
config.saveDir ?? await _getDefaultDownloadDir();
116+
if (saveDirectory.isEmpty) {
117+
return DownloadResult(
118+
status: DownloadStatus.failed,
119+
errorMessage: 'download_no_directory'.tr(),
120+
);
121+
}
117122

118-
// 构建文件路径
119-
String filePath = '$saveDirectory/${config.fileName}';
120-
121-
// 检查文件是否已存在
122-
final File file = File(filePath);
123-
if (file.existsSync() && !config.allowOverwrite) {
124-
// 生成新文件名
125-
final String extension = filePath.split('.').last;
126-
final String baseName = filePath.split('.').take(filePath.split('.').length - 1).join('.');
127-
int counter = 1;
128-
while (File('$baseName($counter).$extension').existsSync()) {
129-
counter++;
130-
}
131-
filePath = '$baseName($counter).$extension';
123+
// 构建文件路径
124+
String filePath = '$saveDirectory/${config.fileName}';
125+
126+
// 检查文件是否已存在
127+
final File file = File(filePath);
128+
if (file.existsSync() && !config.allowOverwrite) {
129+
// 生成新文件名
130+
final String extension = filePath.split('.').last;
131+
final String baseName = filePath
132+
.split('.')
133+
.take(filePath.split('.').length - 1)
134+
.join('.');
135+
int counter = 1;
136+
while (File('$baseName($counter).$extension').existsSync()) {
137+
counter++;
132138
}
139+
filePath = '$baseName($counter).$extension';
140+
}
133141

134-
// 确保目录存在
135-
final Directory directory = Directory(saveDirectory);
136-
if (!directory.existsSync()) {
137-
directory.createSync(recursive: true);
138-
}
142+
// 确保目录存在
143+
final Directory directory = Directory(saveDirectory);
144+
if (!directory.existsSync()) {
145+
directory.createSync(recursive: true);
146+
}
147+
148+
// 开始下载
149+
onStatusChange?.call(DownloadStatus.downloading, 'download_starting'.tr());
139150

140-
// 开始下载
141-
onStatusChange?.call(DownloadStatus.downloading, 'download_starting'.tr());
151+
int retryCount = 0;
152+
bool downloadSuccess = false;
142153

143-
int retryCount = 0;
144-
bool downloadSuccess = false;
154+
// 创建CancelToken并添加到列表
155+
final cancelToken = CancelToken();
156+
_cancelTokens.add(cancelToken);
145157

158+
try {
146159
while (retryCount <= config.maxRetries && !downloadSuccess) {
147160
try {
148161
await _dio.download(
@@ -153,6 +166,7 @@ class DownloadUtils {
153166
receiveTimeout: Duration(seconds: config.timeout),
154167
),
155168
queryParameters: config.queryParams,
169+
cancelToken: cancelToken,
156170
onReceiveProgress: (received, total) {
157171
if (total != -1) {
158172
final double progress = received / total;
@@ -167,10 +181,12 @@ class DownloadUtils {
167181
if (retryCount <= config.maxRetries) {
168182
onStatusChange?.call(
169183
DownloadStatus.pending,
170-
'download_retrying'.tr(namedArgs: {
171-
'attempt': retryCount.toString(),
172-
'max': config.maxRetries.toString(),
173-
}),
184+
'download_retrying'.tr(
185+
namedArgs: {
186+
'attempt': retryCount.toString(),
187+
'max': config.maxRetries.toString(),
188+
},
189+
),
174190
);
175191
await Future.delayed(Duration(milliseconds: config.retryInterval));
176192
} else {
@@ -195,11 +211,17 @@ class DownloadUtils {
195211
downloadedSize: file.lengthSync(),
196212
);
197213
} catch (e) {
198-
onStatusChange?.call(DownloadStatus.failed, 'download_failed'.tr(namedArgs: {'message': e.toString()}));
214+
onStatusChange?.call(
215+
DownloadStatus.failed,
216+
'download_failed'.tr(namedArgs: {'message': e.toString()}),
217+
);
199218
return DownloadResult(
200219
status: DownloadStatus.failed,
201220
errorMessage: e.toString(),
202221
);
222+
} finally {
223+
// 从列表中移除cancelToken
224+
_cancelTokens.remove(cancelToken);
203225
}
204226
}
205227

@@ -218,11 +240,13 @@ class DownloadUtils {
218240
final config = configs[i];
219241
onStatusChange?.call(
220242
DownloadStatus.pending,
221-
'download_preparing_file'.tr(namedArgs: {
222-
'index': (i + 1).toString(),
223-
'total': totalCount.toString(),
224-
'name': config.fileName,
225-
}),
243+
'download_preparing_file'.tr(
244+
namedArgs: {
245+
'index': (i + 1).toString(),
246+
'total': totalCount.toString(),
247+
'name': config.fileName,
248+
},
249+
),
226250
);
227251

228252
final result = await downloadFile(
@@ -300,7 +324,13 @@ class DownloadUtils {
300324

301325
/// 取消所有下载
302326
static void cancelAllDownloads() {
303-
// TODO:Dio 的取消逻辑需要使用 CancelToken
304-
// 这里简化处理,实际项目中可能需要维护一个 CancelToken 列表
327+
// 取消所有下载任务
328+
for (final token in _cancelTokens) {
329+
if (!token.isCancelled) {
330+
token.cancel('Download cancelled by user');
331+
}
332+
}
333+
// 清空列表
334+
_cancelTokens.clear();
305335
}
306336
}

pubspec.yaml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@ publish_to: "none" # 如果你想发布到 pub.dev,请删除此行
1515
# https://developer.apple.com/library/archive/documentation/General/Reference/InfoPlistKeyReference/Articles/CoreFoundationKeys.html
1616
# 在 Windows 中,build-name 用作产品和文件版本的主要、次要和补丁部分
1717
# 而 build-number 用作构建后缀。
18-
version: 1.3.1+210
18+
version: 1.3.1+220
1919

2020
environment:
2121
sdk: ^3.10.4

0 commit comments

Comments
 (0)