<em>Mac</em>Book项目 2009年学校开始实施<em>Mac</em>Book项目,所有师生配备一本<em>Mac</em>Book,并同步更新了校园无线网络。学校每周进行电脑技术更新,每月发送技术支持资料,极大改变了教学及学习方式。因此2011
2021-06-01 09:32:01
一般情況下,下載的功能模組,至少需要提供如下基礎功能:資源下載、取消當前下載、資源是否下載成功、資原始檔的大小、清除快取檔案。而斷點續傳主要體現在取消當前下載後,再次下載時能在之前已下載的基礎上繼續下載。這個能極大程度的減少我們伺服器的頻寬損耗,而且還能為使用者減少流量,避免重複下載,提高使用者體驗。
前置條件:資源必須支援斷點續傳。如何確定可否支援?看看你的伺服器是否支援Range請求即可。
1.定好協定。我們用的http庫是dio;通過校驗md5
檢測檔案快取完整性;關於程式碼中的subDir,設計上認為資源會有多種:音訊、視訊、安裝包等,每種資源分開目錄進行儲存。
import 'package:dio/dio.dart'; typedef ProgressCallBack = void Function(int count, int total); typedef CancelTokenProvider = void Function(CancelToken cancelToken); abstract class AssetRepositoryProtocol { /// 下載單一資源 Future<String> downloadAsset(String url, {String? subDir, ProgressCallBack? onReceiveProgress, CancelTokenProvider? cancelTokenProvider, Function(String)? done, Function(Exception)? failed}); /// 取消下載,Dio中通過CancelToken可控制 void cancelDownload(CancelToken cancelToken); /// 獲取檔案的快取地址 Future<String?> filePathForAsset(String url, {String? subDir}); /// 檢查檔案是否快取成功,簡單對比md5 Future<String?> checkCachedSuccess(String url, {String? md5Str}); /// 檢視快取檔案的大小 Future<int> cachedFileSize({String? subDir}); /// 清除快取 Future<void> clearCache({String? subDir}); }
2.實現抽象協定,其中HttpManagerProtocol內部封裝了dio的相關請求。
class AssetRepository implements AssetRepositoryProtocol { AssetRepository(this.httpManager); final HttpManagerProtocol httpManager; @override Future<String> downloadAsset(String url, {String? subDir, ProgressCallBack? onReceiveProgress, CancelTokenProvider? cancelTokenProvider, Function(String)? done, Function(Exception)? failed}) async { CancelToken cancelToken = CancelToken(); if (cancelTokenProvider != null) { cancelTokenProvider(cancelToken); } final savePath = await _getSavePath(url, subDir: subDir); try { httpManager.downloadFile( url: url, savePath: savePath + '.temp', onReceiveProgress: onReceiveProgress, cancelToken: cancelToken, done: () { done?.call(savePath); }, failed: (e) { print(e); failed?.call(e); }); return savePath; } catch (e) { print(e); rethrow; } } @override void cancelDownload(CancelToken cancelToken) { try { if (!cancelToken.isCancelled) { cancelToken.cancel(); } } catch (e) { print(e); } } @override Future<String?> filePathForAsset(String url, {String? subDir}) async { final path = await _getSavePath(url, subDir: subDir); final file = File(path); if (!(await file.exists())) { return null; } return path; } @override Future<String?> checkCachedSuccess(String url, {String? md5Str}) async { String? path = await _getSavePath(url, subDir: FileType.video.dirName); bool isCached = await File(path).exists(); if (isCached && (md5Str != null && md5Str.isNotEmpty)) { // 存在但是md5驗證不通過 File(path).readAsBytes().then((Uint8List str) { if (md5.convert(str).toString() != md5Str) { path = null; } }); } else if (isCached) { return path; } else { path = null; } return path; } @override Future<int> cachedFileSize({String? subDir}) async { final dir = await _getDir(subDir: subDir); if (!(await dir.exists())) { return 0; } int totalSize = 0; await for (var entity in dir.list(recursive: true)) { if (entity is File) { try { totalSize += await entity.length(); } catch (e) { print('Get size of $entity failed with exception: $e'); } } } return totalSize; } @override Future<void> clearCache({String? subDir}) async { final dir = await _getDir(subDir: subDir); if (!(await dir.exists())) { return; } dir.deleteSync(recursive: true); } Future<String> _getSavePath(String url, {String? subDir}) async { final saveDir = await _getDir(subDir: subDir); if (!saveDir.existsSync()) { saveDir.createSync(recursive: true); } final uri = Uri.parse(url); final fileName = uri.pathSegments.last; return saveDir.path + fileName; } Future<Directory> _getDir({String? subDir}) async { final cacheDir = await getTemporaryDirectory(); late final Directory saveDir; if (subDir == null) { saveDir = cacheDir; } else { saveDir = Directory(cacheDir.path + '/$subDir/'); } return saveDir; } }
3.封裝dio下載,實現資源斷點續傳。
這裡的邏輯比較重點,首先未快取100%的檔案,我們以.temp字尾進行命名,在每次下載時檢測下是否有.temp的檔案,拿到其檔案位元組大小;傳入在header中的range欄位,伺服器就會去解析需要從哪個位置繼續下載;下載全部完成後,再把檔名改回正確的字尾即可。
final downloadDio = Dio(); Future<void> downloadFile({ required String url, required String savePath, required CancelToken cancelToken, ProgressCallback? onReceiveProgress, void Function()? done, void Function(Exception)? failed, }) async { int downloadStart = 0; File f = File(savePath); if (await f.exists()) { // 檔案存在時拿到已下載的位元組數 downloadStart = f.lengthSync(); } print("start: $downloadStart"); try { var response = await downloadDio.get<ResponseBody>( url, options: Options( /// Receive response data as a stream responseType: ResponseType.stream, followRedirects: false, headers: { /// 加入range請求頭,實現斷點續傳 "range": "bytes=$downloadStart-", }, ), ); File file = File(savePath); RandomAccessFile raf = file.openSync(mode: FileMode.append); int received = downloadStart; int total = await _getContentLength(response); Stream<Uint8List> stream = response.data!.stream; StreamSubscription<Uint8List>? subscription; subscription = stream.listen( (data) { /// Write files must be synchronized raf.writeFromSync(data); received += data.length; onReceiveProgress?.call(received, total); }, onDone: () async { file.rename(savePath.replaceAll('.temp', '')); await raf.close(); done?.call(); }, onError: (e) async { await raf.close(); failed?.call(e); }, cancelOnError: true, ); cancelToken.whenCancel.then((_) async { await subscription?.cancel(); await raf.close(); }); } on DioError catch (error) { if (CancelToken.isCancel(error)) { print("Download cancelled"); } else { failed?.call(error); } } }
這篇文章確實沒有技術含量,水一篇,但其實是實用的。這個斷點續傳的實現有幾個注意的點:
以上就是Flutter實現資源下載斷點續傳的範例程式碼的詳細內容,更多關於Flutter資源下載斷點續傳的資料請關注it145.com其它相關文章!
相關文章
<em>Mac</em>Book项目 2009年学校开始实施<em>Mac</em>Book项目,所有师生配备一本<em>Mac</em>Book,并同步更新了校园无线网络。学校每周进行电脑技术更新,每月发送技术支持资料,极大改变了教学及学习方式。因此2011
2021-06-01 09:32:01
综合看Anker超能充系列的性价比很高,并且与不仅和iPhone12/苹果<em>Mac</em>Book很配,而且适合多设备充电需求的日常使用或差旅场景,不管是安卓还是Switch同样也能用得上它,希望这次分享能给准备购入充电器的小伙伴们有所
2021-06-01 09:31:42
除了L4WUDU与吴亦凡已经多次共事,成为了明面上的厂牌成员,吴亦凡还曾带领20XXCLUB全队参加2020年的一场音乐节,这也是20XXCLUB首次全员合照,王嗣尧Turbo、陈彦希Regi、<em>Mac</em> Ova Seas、林渝植等人全部出场。然而让
2021-06-01 09:31:34
目前应用IPFS的机构:1 谷歌<em>浏览器</em>支持IPFS分布式协议 2 万维网 (历史档案博物馆)数据库 3 火狐<em>浏览器</em>支持 IPFS分布式协议 4 EOS 等数字货币数据存储 5 美国国会图书馆,历史资料永久保存在 IPFS 6 加
2021-06-01 09:31:24
开拓者的车机是兼容苹果和<em>安卓</em>,虽然我不怎么用,但确实兼顾了我家人的很多需求:副驾的门板还配有解锁开关,有的时候老婆开车,下车的时候偶尔会忘记解锁,我在副驾驶可以自己开门:第二排设计很好,不仅配置了一个很大的
2021-06-01 09:30:48
不仅是<em>安卓</em>手机,苹果手机的降价力度也是前所未有了,iPhone12也“跳水价”了,发布价是6799元,如今已经跌至5308元,降价幅度超过1400元,最新定价确认了。iPhone12是苹果首款5G手机,同时也是全球首款5nm芯片的智能机,它
2021-06-01 09:30:45