import 'dart:async'; import 'dart:io'; import 'package:dio/dio.dart' as dio; import 'package:dio/dio.dart'; /* * 文件下载 * 懒加载单例 */ class DownLoadManage { //用于记录正在下载的url,避免重复下载 // var downloadingUrls = new List(); var downloadingUrls = new Map(); // 单例公开访问点 factory DownLoadManage() => _getInstance(); // 静态私有成员,没有初始化 static DownLoadManage _instance; // 私有构造函数 DownLoadManage._() { // 具体初始化代码 } Object _assureDioError(err) { if (err is DioError) { return err; } else { var _err = DioError(error: err); return _err; } } // 静态、同步、私有访问点 static DownLoadManage _getInstance() { if (_instance == null) { _instance = DownLoadManage._(); } return _instance; } /* *下载 */ Future download(url, savePath, {ProgressCallback onReceiveProgress, Function done, Function failed}) async { int downloadStart = 0; bool fileExists = false; File f = File(savePath); if (await f.exists()) { downloadStart = f.lengthSync(); fileExists = true; } print("$url $fileExists ${downloadingUrls.containsKey(url) } ${downloadingUrls[url]?.isCancelled} ${downloadingUrls.length}"); if (fileExists && downloadingUrls.containsKey(url) && !downloadingUrls[url].isCancelled) { print("$url $fileExists"); //正在下载 return; } CancelToken cancelToken = new CancelToken(); // downloadingUrls[url] = cancelToken; if (downloadingUrls[url] == null) { downloadingUrls[url] = cancelToken; } var dio = Dio(); String contentLength; int _total=0; try { contentLength = await _getContentLength(dio, url, cancelToken); print("contentLength :$contentLength"); _total = int.parse(contentLength); print("start :$downloadStart -- contentLength: $contentLength"); if (downloadStart >= _total) { //存在本地文件,命中缓存 done(); return; } } catch (e) { print(e); stop(url); return; } cancelToken = new CancelToken(); downloadingUrls[url] = cancelToken; File tmp = File("$savePath.tmp$downloadStart"); tmp.createSync(); var resp = await dio .download(url, tmp.path, deleteOnError: false, onReceiveProgress: (index, total) => onReceiveProgress(index + downloadStart, _total), cancelToken: cancelToken, options: Options( followRedirects: false, headers: {"range": "bytes=$downloadStart-$contentLength"}, )) .whenComplete(() { downloadingUrls.remove(url); }).catchError((e) async { failed.call(e); }); await append(f, tmp); if (resp != null) { done.call(); } // downloadingUrls.remove(url); // done.call(); } append(File file, File tmp) async { IOSink ioSink = file.openWrite(mode: FileMode.writeOnlyAppend); print("save file ---------1111111111111 --- ${tmp.lengthSync()}"); await ioSink.addStream(tmp.openRead()); await tmp.delete(); //删除临时文件 await ioSink.close(); } /* * 获取下载的文件大小 */ Future _getContentLength(Dio dio, url, CancelToken cancelToken) async { try { Response response = await dio.head(url, cancelToken: cancelToken); return response.headers.value(Headers.contentLengthHeader); } catch (e) { print("_getContentLength Failed:" + e.toString()); return 0; } } void stop(String url) { if (downloadingUrls.containsKey(url)) { try { downloadingUrls[url].cancel(); } catch (e) { print(e); downloadingUrls.remove(url); } } } Future _listenCancelForAsyncTask(CancelToken cancelToken, Future future) { if (cancelToken != null && cancelToken.cancelError == null) { // cancelToken.addCompleter(completer); // return Future.any([completer.future, future]).then((result) { // cancelToken.removeCompleter(completer); // return result; // }).catchError((e) { // cancelToken.removeCompleter(completer); // throw e; // }); // } return Future.any([cancelToken.whenCancel, future]).then((value) { // return value; cancelToken.cancel("下载取消"); return value; }).catchError((e) { throw e; }); } else { return future; } } }