Android基于Flutter編寫文件下載管理器
前言
文件下載在很多類型的應(yīng)用中會(huì)涉及,例如音樂(lè)、文檔、包括圖片(只是圖片可以使用一些組件完成無(wú)感知的下載)。本篇介紹使用 Dio 的下載方法完成文件的下載,涉及到的內(nèi)容如下:
- Dio 插件的
download方法介紹; - 使用
download的回調(diào)方法監(jiān)測(cè)下載進(jìn)度; - 使用
CancelToken取消正在下載的任務(wù); - 刪除已下載的文件;
path_provider插件管理App文件目錄;- 下載文件調(diào)試過(guò)程中發(fā)現(xiàn)的一些問(wèn)題;
Dio 的下載方法 download
Dio 的下載方法定義如下:
Future<Response>?download(
??String?urlPath,
??savePath,?{
??ProgressCallback??onReceiveProgress,
??Map<String,?dynamic>??queryParameters,
??CancelToken??cancelToken,
??bool?deleteOnError?=?true,
??String?lengthHeader?=?Headers.contentLengthHeader,
??data,
??Options??options,
});urlPath:網(wǎng)絡(luò)資源的url;savePath:dynamic類型,可以是下載后存儲(chǔ)文件路徑的字符串,也可以是一個(gè)返回字符串的回調(diào)方法(Dio 會(huì)把headers參數(shù)攜帶過(guò)去,方便針對(duì)下載返回內(nèi)容構(gòu)建文件路徑);onReceiveProgress:文件接收進(jìn)度,是一個(gè)void Function(int count, int total)回調(diào)函數(shù),調(diào)用者可以通過(guò)該回調(diào)方法監(jiān)測(cè)下載進(jìn)度。deleteOnError:發(fā)生錯(cuò)誤時(shí)候是否刪除已下載的文件,默認(rèn)是 true。lengthHeader:源文件的實(shí)際大?。ㄎ磯嚎s前)。默認(rèn)是header的content-length。如果文件壓縮了,而沒有指定該值的話,那進(jìn)度回調(diào)里的total會(huì)是-1;如果使用自定義的header指定了文件的大小,那么total會(huì)是自定義的header對(duì)應(yīng)的文件大小。- 其他參數(shù)和普通的請(qǐng)求差不多,這里不再贅述。
為了不暴露下載的具體實(shí)現(xiàn),我們?cè)?nbsp;http_util.dart 中封裝一個(gè)自己的下載方法。
static?Future?download(
??String?url,
??String?savePath,?{
??Map<String,?dynamic>?queryParams,
??CancelToken?cancelToken,
??dynamic?data,
??Options?options,
??void?Function(int,?int)?onReceiveProgress,
})?async?{
??try?{
????return?await?_dioInstance.download(
??????url,
??????savePath,
??????queryParameters:?queryParams,
??????cancelToken:?cancelToken,
??????onReceiveProgress:?onReceiveProgress,
????);
??}?on?DioError?catch?(e)?{
????if?(CancelToken.isCancel(e))?{
??????EasyLoading.showInfo('下載已取消!');
????}?else?{
??????if?(e.response?!=?null)?{
????????_handleErrorResponse(e.response);
??????}?else?{
????????EasyLoading.showError(e.message);
??????}
????}
??}?on?Exception?catch?(e)?{
????EasyLoading.showError(e.toString());
??}
}
監(jiān)測(cè)下載進(jìn)度
我們新建一個(gè)文件下載頁(yè)面 file_download.dart完成文件下載的示例。這里定義了幾個(gè)屬性來(lái)對(duì)文件下載過(guò)程進(jìn)行反饋:
//?文件下載地址,這里是谷歌瀏覽器的下載地址(Mac?版本) String?_downloadPath?= ??????'https://dl.google.com/chrome/mac/stable/GGRO/googlechrome.dmg'; //?下載進(jìn)度比例,用于檢測(cè)下載是否完成 double?_downloadRatio?=?0.0; //?下載進(jìn)度百分比 String?_downloadIndicator?=?'0.00%'; //?下載文件的存儲(chǔ)路徑 String?_destPath; //?取消下載的?token CancelToken?_token; //?指示當(dāng)前是否處于下載中,以便做業(yè)務(wù)判斷 bool?_downloading?=?false;
然后我們定義一個(gè)下載方法,在下載過(guò)程中如果 total 不為-1就更新下載進(jìn)度,否則提示錯(cuò)誤(實(shí)際調(diào)試發(fā)現(xiàn),如果涉及到需要驗(yàn)證的,下載后后端實(shí)際會(huì)返回網(wǎng)頁(yè),這樣也能下載網(wǎng)頁(yè)內(nèi)容下來(lái),但是不是想要的文件)。
void?_downloadFile()?{
??_token?=?CancelToken();
??_downloading?=?true;
??HttpUtil.download(_downloadPath,?_destPath,?cancelToken:?_token,
??????onReceiveProgress:?(int?received,?int?total)?{
????if?(total?!=?-1)?{
??????if?(!_token.isCancelled)?{
????????setState(()?{
??????????_downloadRatio?=?(received?/?total);
??????????if?(_downloadRatio?==?1)?{
????????????_downloading?=?false;
??????????}
??????????_downloadIndicator?=
??????????????(_downloadRatio?*?100).toStringAsFixed(2)?+?'%';
????????});
??????}
????}?else?{
??????_downloading?=?false;
??????EasyLoading.showError('無(wú)法獲取文件大小,下載失敗!');
????}
??});
}這里因?yàn)樯婕暗娇赡苋∠?,因此只有在沒有取消的情況下才更新下載狀態(tài),要不可能會(huì)出現(xiàn)取消的時(shí)候還處在下載接收字節(jié)的過(guò)程中,雖然取消了但是看到下載進(jìn)度還在走的情況。
取消下載
取消下載其實(shí)很簡(jiǎn)單,當(dāng)我們點(diǎn)擊取消按鈕的時(shí)候,調(diào)用 CancelToken 的cancel方法即可。這里我們做了一個(gè)判斷,下載比例低于1才可以取消,因?yàn)橄螺d完成再取消會(huì)拋出異常。同時(shí)取消后重置下載比例和顯示的下載百分比。
void?_cancelDownload()?{
??if?(_downloadRatio?<?1.0)?{
????_token.cancel();
????_downloading?=?false;
????setState(()?{
??????_downloadRatio?=?0;
??????_downloadIndicator?=?'0.00%';
????});
??}
}
刪除已經(jīng)下載的文件
對(duì)于 App,沒有別的入口管理文件,因此實(shí)際過(guò)程中我們需要提供下載入口供用戶清理已下載的文件。實(shí)際已下載的文件,我們需要有下載文件管理功能供用戶管理文件,這個(gè)時(shí)候會(huì)需要本地存儲(chǔ)支撐,我們?cè)诤罄m(xù)的章節(jié)會(huì)介紹本地存儲(chǔ)。
刪除文件前需要判斷文件是否存在,如果文件不存在刪除可能拋出異常。文件的管理使用的是 dart:io 中的方法。
void?_deleteFile()?{
??try?{
????File?downloadedFile?=?File(_destPath);
????if?(downloadedFile.existsSync())?{
??????downloadedFile.delete();
????}?else?{
??????EasyLoading.showError('文件不存在');
????}
??}?catch?(e)?{
????EasyLoading.showError(e.toString());
??}
}path_provider文件目錄管理
在 App 中沒法直接知道應(yīng)用的文件存儲(chǔ)目錄,因此需要借用 path_provider 插件來(lái)獲取 App 的文件存儲(chǔ)目錄,path_provider 提供了如下方法:
getTemporaryDirectory:應(yīng)用臨時(shí)目錄(可能被清除)getApplicationDocumentsDirectory:應(yīng)用文檔目錄(不會(huì)被系統(tǒng)清除,主要用戶數(shù)據(jù)存儲(chǔ)目錄),對(duì)于安卓推薦使用外部存儲(chǔ)getExternalStorageDirectory。getApplicationSupportDirectory:應(yīng)用支持目錄,一般放置與用戶無(wú)關(guān)的數(shù)據(jù)。getLibraryDirectory:指向應(yīng)用可以持久存儲(chǔ)數(shù)據(jù)的目錄,不支持安卓平臺(tái)。getExternalStorageDirectory:獲取外部存儲(chǔ)目錄,不支持 iOS 平臺(tái)。getExternalCacheDirectories:獲取外部緩存目錄,,不支持 iOS 平臺(tái)。getExternalStorageDirectories:獲取外部可以的目錄列表,不支持 iOS 平臺(tái)。getDownloadsDirectory:獲取下載目錄,用于 Web 端,不支持安卓和 iOS平臺(tái)。
通過(guò) path_provider拿到Directory對(duì)象后,就可以通過(guò) Directory的 path 屬性獲取到完整的目錄路徑。本例我們是在 initialState 里獲取文件存儲(chǔ)路徑的,使用的是臨時(shí)目錄。
void?initState()?{
??getTemporaryDirectory()
??????.then((tempDir)?=>?{_destPath?=?tempDir.path?+?'googlechrome.dmg'});
??super.initState();
}
調(diào)試過(guò)程中遇到的一些錯(cuò)誤
OS Error: Read-only file system:安卓系統(tǒng)需要獲取READ_EXTERNAL_STORAGE和WRITE_EXTERNAL_STORAGE權(quán)限。同時(shí)需要使用path_provider獲取應(yīng)用的文件目錄再往對(duì)應(yīng)的目錄讀寫文件和訪問(wèn)文件目錄。onReceivedProgress中如果total=-1則表示該文件被壓縮或者需要會(huì)話信息才可以下載(如后端開啟了驗(yàn)證)。- 刪除文件的時(shí)候需要檢查文件是否在下載過(guò)程中,如果在下載過(guò)程中刪除會(huì)引起文件讀寫沖突,拋出異常。
CancelToken一個(gè)實(shí)例只能取消一次請(qǐng)求,因此每次發(fā)起請(qǐng)求的時(shí)候需要重新構(gòu)建CancelToken對(duì)象,否則取消一次后無(wú)法再次取消。
運(yùn)行結(jié)果及代碼
運(yùn)行結(jié)果如下圖所示:

屏幕錄制2021-07-24 下午9.59.04.gif
總結(jié)
從示例可以看到,Dio 的下載方法是簡(jiǎn)單易用的,而且提供了友好的下載反饋。同時(shí),借助 CancelToken 也能取消或者暫停下載(暫停時(shí)設(shè)置deleteOnError=false,表示不刪除文件,然后恢復(fù)到時(shí)候從已下載的偏移量開始,也可以按這種方式做斷點(diǎn)續(xù)傳,具體方式可以搜索或者自己完成)。Dio 網(wǎng)絡(luò)請(qǐng)求系列的詳細(xì)介紹到這一篇結(jié)束,后續(xù)實(shí)際業(yè)務(wù)用到的時(shí)候再穿插介紹,接下來(lái)一篇將對(duì) Dio 系列文章做一個(gè)整體總結(jié)。
以上就是Android基于Flutter編寫文件下載管理器的詳細(xì)內(nèi)容,更多關(guān)于Android文件下載的資料請(qǐng)關(guān)注腳本之家其它相關(guān)文章!
相關(guān)文章
Android實(shí)現(xiàn)支付寶螞蟻森林水滴浮動(dòng)效果
這篇文章主要為大家詳細(xì)介紹了Android實(shí)現(xiàn)支付寶螞蟻森林水滴浮動(dòng)效果,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2018-06-06
Android自定義TextBanner實(shí)現(xiàn)自動(dòng)滾動(dòng)
這篇文章主要為大家詳細(xì)介紹了Android自定義TextBanner實(shí)現(xiàn)自動(dòng)滾動(dòng),文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2020-07-07
Android自定義View實(shí)現(xiàn)隨手勢(shì)滑動(dòng)控件
這篇文章主要為大家詳細(xì)介紹了Android自定義View實(shí)現(xiàn)隨手勢(shì)滑動(dòng)的控件,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2017-02-02
在啟動(dòng)欄制作android studio啟動(dòng)圖標(biāo)
這篇文章主要介紹了在啟動(dòng)欄制作android studio啟動(dòng)圖標(biāo)的相關(guān)知識(shí),需要的朋友可以參考下2018-03-03
詳解Android XML中引用自定義內(nèi)部類view的四個(gè)why
本篇文章主要介紹了詳解Android XML中引用自定義內(nèi)部類view,小編覺得挺不錯(cuò)的,現(xiàn)在分享給大家,也給大家做個(gè)參考。2016-12-12
Android模仿實(shí)現(xiàn)閑魚首頁(yè)的思路與方法
這篇文章主要給大家介紹了Android模仿實(shí)現(xiàn)閑魚首頁(yè)的思路與方法,文中通過(guò)示例代碼介紹的非常詳細(xì),并在文末給出了完整的代碼供大家參考學(xué)習(xí),需要的朋友們下面來(lái)一起看看吧。2017-04-04
Android圖片選擇器ImageEditContainer
這篇文章主要為大家詳細(xì)介紹了Android圖片選擇器ImageEditContainer的相關(guān)資料,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2017-07-07

