一、需求背景
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