一、需求背景
1、需要實現小程序下載最大500M視頻
2、同時需支持圖片下載
3、退到其他頁面再次回到當前頁面時,下載進度也需要展示
二、實現步驟
1、在app.ts文件定義一個全局變量globalDownLoadData
2、寫一個獨立的下載hooks,代碼如下(hooks/useDownLoad.ts文件)
import Taro, { useDidShow } from '@tarojs/taro';
import { useState, useCallback, useEffect, useRef } from 'react';
import { tips } from '@/modules/utils/log';
import { GET_ENV } from '@/modules/core/env';// 下載選項接口
interface DownloadOptions {/** 下載文件的URL */url: string;// 下載的是圖片還是視頻,默認是視頻isImg?: boolean;// 下載成功后的回調successFn?: () => void;
}// 請注意_taskId格式應該為:`material_${routerParams.id}`
// material為頁面標志,請注意唯一性,避免不同頁面的id重復
export const useDownLoad = _taskId => {const taskId = `${GET_ENV()}_${_taskId}`;const globalDownLoadData = useRef(Taro.getApp().globalDownLoadData);const [progress, setProgress] = useState(0);useDidShow(() => {// 恢復目標頁面下載進度setProgress(globalDownLoadData.current[taskId]?.progress || 0);});useEffect(() => {globalDownLoadData.current[taskId] = {progress,downloadTask: globalDownLoadData.current[taskId]?.downloadTask};}, [progress]);// 賦值數據給全局useEffect(() => {Taro.getApp().globalDownLoadData = globalDownLoadData.current;}, [globalDownLoadData]);useEffect(() => {// 再次進入頁面時,將監聽加上addProgressUpdate();}, []);/*** 執行文件下載* @param options 下載選項*/const downloadFn = useCallback(async (options: DownloadOptions) => {try {setProgress(0);// 創建下載任務globalDownLoadData.current[taskId].downloadTask = Taro.downloadFile({url: options.url,success: async res => {if (res.statusCode === 200) {// 下載成功,保存到相冊Taro.getSetting({success(settingRes) {// 是否相冊授權,已授權直接保存圖片if (settingRes.authSetting['scope.writePhotosAlbum']) {const Api = options.isImg ? 'saveImageToPhotosAlbum' : 'saveVideoToPhotosAlbum';Taro[Api]({filePath: res.tempFilePath,success() {tips('下載完成');options.successFn?.();// 下載完成后清除進度setProgress(0);},fail(err) {console.log('saveImageToPhotosAlbum err', err);setProgress(0);}});// 未授權,則先授權} else {Taro.authorize({scope: 'scope.writePhotosAlbum',fail() {tips('下載失敗,請先點擊右上角獲取授權');setProgress(0);}});}},fail(err) {setProgress(0);tips('下載失敗,請稍后再試');console.log('getSetting err', err);}});} else {// 下載失敗setProgress(0);tips('下載失敗,請稍后再試');console.log('downloadFile下載出錯了:', res);}},fail: error => {// 下載出錯setProgress(0);globalDownLoadData.current[taskId] = {progress: 0,downloadTask: null};console.log('下載出錯了:', error);Taro.showModal({title: '下載異常',content: '下載異常或文件大小超過小程序限制,請通過瀏覽器下載!',confirmColor: '#3f57ff',success: modalRes => {if (modalRes.confirm) {Taro.setClipboardData({data: options.url,success() {tips('資源鏈接已復制');}});}}});}});addProgressUpdate();} catch (error) {// 捕獲其他異常tips('下載失敗,請稍后再試');console.log('downloadFn下載失敗======>', error);setProgress(0);}}, []);// 監聽下載進度變化const addProgressUpdate = () => {const downloadTask = globalDownLoadData.current[taskId]?.downloadTask;if (!downloadTask) {setProgress(0);return;}downloadTask.onProgressUpdate(res => {if (res.progress >= 100) {// 下載完成時,延遲2秒后清除進度setTimeout(() => {setProgress(0);downloadTask.abort();delete globalDownLoadData.current[taskId];}, 2000);} else {setProgress(res.progress);}});};return {downloadFn,progress};
};
3、頁面使用
import { useDownLoad } from '@/subPages/hooks/useDownLoad';const { downloadFn, progress } = useDownLoad(`material_${routerParams.id}`);const onDownload = () => {if (!curUrl) {tips('下載資源不存在');return;}Taro.showModal({title: '提示',confirmColor: '#3f57ff',content: '確定要下載該素材嗎?',success: async res => {if (res.confirm) {downloadFn({url: curUrl,isImg: false});}}});};
四、參考文檔:
https://developers.weixin.qq.com/miniprogram/dev/api/network/download/wx.downloadFile.html
https://developers.weixin.qq.com/miniprogram/dev/api/media/image/wx.saveImageToPhotosAlbum.html
https://developers.weixin.qq.com/miniprogram/dev/api/media/video/wx.saveVideoToPhotosAlbum.html