概览
uniapp实现多文件下载,保存到本地,因为使用的是uni.downloadFile 实现文件的下载,每次只能下载一个,需要下载多个文件,并保存到本地,并把保存的地址存储到对应的数据组中,并实现进度条显示。
需求分析
1、文件下载并保存到本地 直接使用uni.downloadFile 和uni.saveFile 两个方法既可以实现。
2、如有多个文件下载,需要异步一个一个去下载,并把保存到本地的路径赋值给传过来的数据。
3、如果使用for循环,会存存储数据混乱的情况,或者说直接保存的路径只保存到了最后一个数组对象中,无法实现保存对应的存储路径到数组。
4、进度条显示,根据下载的方法,进行监听进度返回到页面进行显示进度条的变化。
具体实现
download.js 代码
// +----------------------------------------------------------------------
// | 下载工具类
// +----------------------------------------------------------------------
import {HTTP_REQUEST_URL
} from '@/config/app';
export const hostUrlUpLoad = HTTP_REQUEST_URL + '/upload/';
export const hostUrl = HTTP_REQUEST_URL;
import {getStudyData,getTestData
} from '@/api/api.js';/*** 下载分类相关数据* 图片加载到本地* @returns boolean*/
export async function downloadClass(data, inta) {if (data.length > 0 && inta < data.length) {try {const downloadTask = await uni.downloadFile({url: data[inta].icon_path.includes('upload') ? hostUrl + data[inta].icon_path :hostUrlUpLoad + data[inta].icon_path,success: res => {if (res.statusCode === 200) {uni.saveFile({tempFilePath: res.tempFilePath, // 下载文件的临时路径success: saveRes => {data[inta].icon_path = saveRes.savedFilePathif (inta < data.length - 1) {inta = inta + 1downloadClass(data, inta)} else {uni.setStorageSync('classData', data)}},fail: err => {if (inta < data.length - 1) {inta = inta + 1downloadClass(data, inta)} else {uni.setStorageSync('classData', data)}}});} else {if (inta < data.length - 1) {inta = inta + 1downloadClass(data, inta)} else {uni.setStorageSync('classData', data)}}},fail: err => {if (inta < data.length - 1) {inta = inta + 1downloadClass(data, inta)} else {uni.setStorageSync('classData', data)}}});downloadTask.onProgressUpdate(resda => {})} catch (e) {if (inta < data.length - 1) {inta = inta + 1downloadClass(data, inta)} else {uni.setStorageSync('classData', data)}//TODO handle the exception}}
}/*** 下载科目数据* 图片加载到本地* @returns boolean*/
export async function downloadSubject(data, inta, callBack) {var callBackFun = callBack;if (data.length > 0 && inta < data.length) {try {const downloadTask = await uni.downloadFile({url: data[inta].icon_path.includes('upload') ? hostUrl + data[inta].icon_path :hostUrlUpLoad + data[inta].icon_path,success: res => {if (res.statusCode === 200) {uni.saveFile({tempFilePath: res.tempFilePath, // 下载文件的临时路径success: saveRes => {data[inta].icon_path = saveRes.savedFilePathif (inta < data.length - 1) {inta = inta + 1downloadSubject(data, inta, callBackFun)} else {uni.setStorageSync('SubjectData', data, callBackFun)requestSubjectClass(data, 0, callBackFun)}},fail: err => {if (inta < data.length - 1) {inta = inta + 1downloadSubject(data, inta, callBackFun)} else {uni.setStorageSync('SubjectData', data)requestSubjectClass(data, 0, callBackFun)}}});} else {if (inta < data.length - 1) {inta = inta + 1downloadSubject(data, inta, callBackFun)} else {uni.setStorageSync('SubjectData', data)requestSubjectClass(data, 0, callBackFun)}}},fail: err => {if (inta < data.length - 1) {inta = inta + 1downloadSubject(data, inta, callBackFun)} else {uni.setStorageSync('SubjectData', data)requestSubjectClass(data, 0, callBackFun)}}});downloadTask.onProgressUpdate(resda => {callBackFun(resda.progress, false)})} catch (e) {//TODO handle the exceptionif (inta < data.length - 1) {inta = inta + 1downloadSubject(data, inta, callBackFun)} else {uni.setStorageSync('SubjectData', data)requestSubjectClass(data, 0, callBackFun)}}}
}/*** 根据科目列表赋值分类数据* @param {Object} data* @param {Object} inta*/
export async function requestSubjectClass(data, inta, callBack) {const callBackFun = callBack;if (data.length > 0 && inta < data.length) {try {await getStudyData(data[inta].id).then(res => {if (res.data.code == 200 && res.data.status == 'success' && res.data.data.count > 0) {data[inta].categoryData = res.data.data.listif (inta < data.length - 1) {inta = inta + 1requestSubjectClass(data, inta, callBackFun)} else {uni.setStorageSync('SubjectData', data)downloadSubjectFile(data, 0, 0, callBackFun)}} else {if (inta < data.length - 1) {inta = inta + 1requestSubjectClass(data, inta, callBackFun)} else {uni.setStorageSync('SubjectData', data)downloadSubjectFile(data, 0, 0, callBackFun)}}})} catch (e) {//TODO handle the exceptionif (inta < data.length - 1) {inta = inta + 1requestSubjectClass(data, inta, callBackFun)} else {uni.setStorageSync('SubjectData', data)downloadSubjectFile(data, 0, 0, callBackFun)}}}
}/*** 下载文件到本地* @returns boolean*/
export async function downloadSubjectFile(data, inta, intaClass, callBack) {const callBackFun = callBack;if (data.length > 0 && inta < data.length) {if (data[inta].categoryData.length > 0 && intaClass < data[inta].categoryData.length) {try {var filePath = ''if (data[inta].categoryData[intaClass].file_path.includes('upload')) {if (data[inta].categoryData[intaClass].file_path.charAt(0) == '/') {filePath = hostUrl + data[inta].categoryData[intaClass].file_path} else {filePath = "http://" + data[inta].categoryData[intaClass].file_path}} else {filePath = hostUrlUpLoad + data[inta].categoryData[intaClass].file_path}const downloadTask = await uni.downloadFile({url: filePath,success: res => {if (res.statusCode === 200) {uni.saveFile({tempFilePath: res.tempFilePath, // 下载文件的临时路径success: saveRes => {data[inta].categoryData[intaClass].file_path = saveRes.savedFilePathif (intaClass < data[inta].categoryData.length - 1) {intaClass = intaClass + 1downloadSubjectFile(data, inta, intaClass,callBackFun)} else {if (inta < data.length - 1) {inta = inta + 1downloadSubjectFile(data, inta, 0, callBackFun)} else {uni.setStorageSync('SubjectData', data)callBackFun(100, true)}}},fail: err => {console.log("保存文件失败 ")if (intaClass < data[inta].categoryData.length - 1) {intaClass = intaClass + 1downloadSubjectFile(data, inta, intaClass,callBackFun)} else {if (inta < data.length - 1) {inta = inta + 1downloadSubjectFile(data, inta, 0, callBackFun)} else {uni.setStorageSync('SubjectData', data)callBackFun(100, true)}}}});} else {if (intaClass < data[inta].categoryData.length - 1) {intaClass = intaClass + 1downloadSubjectFile(data, inta, intaClass, callBackFun)} else {if (inta < data.length - 1) {inta = inta + 1downloadSubjectFile(data, inta, 0, callBackFun)} else {uni.setStorageSync('SubjectData', data)callBackFun(100, true)}}}},fail: err => {console.log("下载失败 fail ")if (intaClass < data[inta].categoryData.length - 1) {intaClass = intaClass + 1downloadSubjectFile(data, inta, intaClass, callBackFun)} else {if (inta < data.length - 1) {inta = inta + 1downloadSubjectFile(data, inta, 0, callBackFun)} else {uni.setStorageSync('SubjectData', data)callBackFun(100, true)}}}});downloadTask.onProgressUpdate(resda => {callBackFun(resda.progress, false)})} catch (e) {//TODO handle the exceptionconsole.log("downloadSubjectFile catch " + JSON.stringify(e))if (intaClass < data[inta].categoryData.length - 1) {intaClass = intaClass + 1downloadSubjectFile(data, inta, intaClass, callBackFun)} else {if (inta < data.length - 1) {inta = inta + 1downloadSubjectFile(data, inta, 0, callBackFun)} else {console.log("打印保存的科目数据 downloadSubjectFile " + JSON.stringify(data))uni.setStorageSync('SubjectData', data)callBackFun(100, true)}}}} else {if (intaClass < data[inta].categoryData.length - 1) {intaClass = intaClass + 1downloadSubjectFile(data, inta, intaClass, callBackFun)} else {if (inta < data.length - 1) {inta = inta + 1downloadSubjectFile(data, inta, 0, callBackFun)} else {console.log("打印保存的科目数据 downloadSubjectFile " + JSON.stringify(data))uni.setStorageSync('SubjectData', data)callBackFun(100, true)}}}}
}/*** 根据科目列表获取题库* @param {Object} subjectData*/
export async function requestSubjectTestData(subjectData) {try {await getTestData(subjectData.id).then(res => {if (res.data.code == 200 && res.data.status == 'success') {uni.removeStorageSync('CNLLIST' + subjectData.id)uni.removeStorageSync('CNLLISTCOUNT' + subjectData.id)uni.removeStorageSync('ENLLIST' + subjectData.id)uni.removeStorageSync('ENLLISTCOUNT' + subjectData.id)uni.setStorageSync('CNLLIST' + subjectData.id, res.data.data.list)uni.setStorageSync('CNLLISTCOUNT' + subjectData.id, res.data.data.count)uni.setStorageSync('ENLLIST' + subjectData.id, res.data.data.english_list)uni.setStorageSync('ENLLISTCOUNT' + subjectData.id, res.data.data.english_count)} else {uni.removeStorageSync('CNLLIST' + subjectData.id)uni.removeStorageSync('CNLLISTCOUNT' + subjectData.id)uni.removeStorageSync('ENLLIST' + subjectData.id)uni.removeStorageSync('ENLLISTCOUNT' + subjectData.id)}})} catch (e) {console.log('报错信息 ' + JSON.stringify(subjectData.id))//TODO handle the exception}
}/*** 根据科目列表赋值分类数据* @param {Object} data* @param {Object} inta*/
export async function requestSubjectClassSize(data, inta, callBack) {if (data.length > 0 && inta < data.length) {try {await getStudyData(data[inta].id).then(res => {if (res.data.code == 200 && res.data.status == 'success' && res.data.data.count > 0) {data[inta].categoryData = res.data.data.listif (inta < data.length - 1) {inta = inta + 1requestSubjectClassSize(data, inta, callBack)} else {requestDownloadFileSize(data, 0, 0,0, callBack)}} else {if (inta < data.length - 1) {inta = inta + 1requestSubjectClassSize(data, inta, callBack)} else {requestDownloadFileSize(data, 0, 0,0, callBack)}}})} catch (e) {//TODO handle the exceptionif (inta < data.length - 1) {inta = inta + 1requestSubjectClassSize(data, inta, callBack)} else {requestDownloadFileSize(data, 0, 0,0, callBack)}}}
}/*** 下载文件总大小* @returns boolean*/
export async function requestDownloadFileSize(data, inta, intaClass, contentLength, callBack) {if (data.length > 0 && inta < data.length) {if (data[inta].categoryData.length > 0 && intaClass < data[inta].categoryData.length) {try {var filePath = ''if (data[inta].categoryData[intaClass].file_path.includes('upload')) {if (data[inta].categoryData[intaClass].file_path.charAt(0) == '/') {filePath = hostUrl + data[inta].categoryData[intaClass].file_path} else {filePath = "http://" + data[inta].categoryData[intaClass].file_path}} else {filePath = hostUrlUpLoad + data[inta].categoryData[intaClass].file_path}await uni.request({url: filePath,method: 'head',// responseType: 'arraybuffer', // 或者其他如'text'、'blob'等,取决于你需要的内容类型// header: {// 'Accept-Encoding': 'gzip, deflate'// },success(res) {if (res.statusCode === 200) {if (intaClass < data[inta].categoryData.length - 1) {intaClass = intaClass + 1if(res.header['Content-Length'] || res.header['content-length']){console.log("filePath " + filePath + ' ' + JSON.stringify(res.header['Content-Length']))contentLength = contentLength + parseInt(res.header['Content-Length']?res.header['Content-Length']:res.header['content-length'],10); // 获取Content-Length字段的值}requestDownloadFileSize(data, inta, intaClass,contentLength,callBack)} else {if (inta < data.length - 1) {inta = inta + 1if(res.header['Content-Length'] || res.header['content-length']){contentLength = contentLength + parseInt(res.header['Content-Length']?res.header['Content-Length']:res.header['content-length'],10); // 获取Content-Length字段的值}requestDownloadFileSize(data, inta, 0,contentLength, callBack)} else {callBack(contentLength)}}} else {if (intaClass < data[inta].categoryData.length - 1) {intaClass = intaClass + 1requestDownloadFileSize(data, inta, intaClass,contentLength, callBack)} else {if (inta < data.length - 1) {inta = inta + 1requestDownloadFileSize(data, inta, 0,contentLength, callBack)} else {callBack(contentLength)}}}},error(err) {console.error('请求错误:', err);if (intaClass < data[inta].categoryData.length - 1) {intaClass = intaClass + 1requestDownloadFileSize(data, inta, intaClass,contentLength, callBack)} else {if (inta < data.length - 1) {inta = inta + 1requestDownloadFileSize(data, inta, 0,contentLength, callBack)} else {callBack(contentLength)}}},networkTimeout: 5000, // 设置超时时间timeout: 5000 // 设置整体请求超时时间})} catch (e) {//TODO handle the exceptionconsole.log("requestDownloadFileSize catch " + JSON.stringify(e))if (intaClass < data[inta].categoryData.length - 1) {intaClass = intaClass + 1requestDownloadFileSize(data, inta, intaClass,contentLength, callBack)} else {if (inta < data.length - 1) {inta = inta + 1requestDownloadFileSize(data, inta, 0,contentLength, callBack)} else {callBack(contentLength)}}}} else {if (intaClass < data[inta].categoryData.length - 1) {intaClass = intaClass + 1requestDownloadFileSize(data, inta, intaClass,contentLength, callBack)} else {if (inta < data.length - 1) {inta = inta + 1requestDownloadFileSize(data, inta, 0,contentLength, callBack)} else {callBack(contentLength)}}}}
}/*** @param {Object} data* @param {Object} inta* @param {Object} contentLength* @param {Object} callBack*/
export async function requestFileSize(data, inta, contentLength, callBack) {if (data.length > 0 && inta < data.length) {try {uni.request({url: downloadUrl,method: 'GET',responseType: 'arraybuffer', // 或者其他如'text'、'blob'等,取决于你需要的内容类型header: {'Accept-Encoding': 'gzip, deflate'},success(res) {if (res.statusCode === 200 && res.data) {if (inta < data.length - 1) {inta = inta + 1if(res.header['Content-Length'] || res.header['content-length']){contentLength = contentLength + parseInt(res.header['Content-Length']?res.header['Content-Length']:res.header['content-length'],10); // 获取Content-Length字段的值}console.log(contentLength + ' 字节');requestFileSize(data, inta, contentLength, callBack)} else {callBack(contentLength)}} else {if (inta < data.length - 1) {inta = inta + 1requestFileSize(data, inta, contentLength, callBack)} else {callBack(contentLength)}}},error(err) {console.error('请求错误:', err);if (inta < data.length - 1) {inta = inta + 1requestFileSize(data, inta, contentLength, callBack)} else {callBack(contentLength)}},networkTimeout: 5000, // 设置超时时间timeout: 5000 // 设置整体请求超时时间});} catch (e) {//TODO handle the exceptionif (inta < data.length - 1) {inta = inta + 1requestFileSize(data, inta, contentLength, callBack)} else {callBack(contentLength)}}}
}export default {downloadClass,downloadSubject,requestSubjectTestData,requestDownloadFileSize,requestSubjectClassSize
};
实现的逻辑是,下载完成一个文件,然后把保存到本地的路径存储到传过来对应的数据中,把原来线上的文件地址替换为本地路径,然后在进行下一个。
进度条显示
<template><view style="width: 100%;height: 100%;display: flex;align-items: center;justify-content: center;position: relative;"><view style="position: absolute;top: 80rpx;display: flex;flex-direction: column;align-items: center;justify-content: center;"><image src="../../static/kaoshilogo.png" style="width: 80rpx;" mode="widthFix"></image><view style="margin-top: 20rpx;font-family: Source Han Sans-Bold;font-size: 18rpx;">{{language.AppName}}</view></view><view style="position: absolute;bottom: 30rpx;width: 100%;display: flex;align-items: center;justify-content: center;flex-direction: column;"><view style="width: 60%;font-size: 8rpx;text-align: center;margin-bottom: 3rpx;">{{language.zhengzaigengxinziliao}}</view><view class="progress-bar"><view class="progress-bar__fill" :style="{width: progress}"></view></view></view></view>
</template><script>import {getSubjectData,getClassData,getList,uploadStudyTime,uploadTestData,uploadOpen} from '@/api/api.js'export default {data() {return {fileManager: null,openData: [],progress: '',language: getApp().globalData.language,}},onLoad() {this.uploadOpen()//获取总分类数据if (!uni.getStorageSync("classData")) {this.getClassDataPage()}//获取科目数据if (!uni.getStorageSync("SubjectData")) {this.getSubjectDataPage()}else{if(uni.getStorageSync('userInfo')){uni.switchTab({url: '/pages/home/home'})}else{uni.navigateTo({url: '/pages/login/login'})}}if(uni.getStorageSync('userInfo') && uni.getStorageSync('userInfo').userName != 'Guest'){if(uni.getStorageSync('uploadTime')){this.uploadSyudyTime()}if(uni.getStorageSync('uploadTestTime')){this.uploadTestTime()}}},methods: {async getClassDataPage(){var that = thisawait getClassData().then(res => {if (res.data.code == 200 && res.data.status == 'success') {that.$download.downloadClass(res.data.data.list, 0)} else if (res.statusCode == '404' || res.status == 1) {console.log("网路请求失败")} else {console.log(res)}})},async getSubjectDataPage(){var that = thisawait getSubjectData().then(res => {if (res.data.code == 200 && res.data.status == 'success') {if(res.data && res.data.data && res.data.data.list && res.data.data.list.length > 0){for (var i = 0; i < res.data.data.list.length; i++) {this.$download.requestSubjectTestData(res.data.data.list[i]);}this.$download.downloadSubject(res.data.data.list, 0,function(progress,boobleTo){that.progress = progress + "%"if(boobleTo){getApp().globalData.version = '1.0.' + that.$utils.formatDateTime()if(uni.getStorageSync('userInfo')){uni.switchTab({url: '/pages/home/home'})}else{uni.navigateTo({url: '/pages/login/login'})}}});}else{if(uni.getStorageSync('userInfo')){uni.switchTab({url: '/pages/home/home'})}else{uni.navigateTo({url: '/pages/login/login'})}}} else if (res.statusCode == '404' || res.status == 1) {console.log("网路请求失败")uni.showToast({title: that.language.startToastnetworkMsg,icon: 'none',duration: 1500})if(uni.getStorageSync('userInfo')){uni.switchTab({url: '/pages/home/home'})}else{uni.navigateTo({url: '/pages/login/login'})}} else {uni.showToast({title: that.language.startToastnetworkMsg,icon: 'none',duration: 1500})if(uni.getStorageSync('userInfo')){uni.switchTab({url: '/pages/home/home'})}else{uni.navigateTo({url: '/pages/login/login'})}}})},async uploadSyudyTime(){await uploadStudyTime({"data": uni.getStorageSync('uploadTime')}).then(res=>{if(res.data.code == 200 && res.data.status == 'success'){uni.removeStorageSync('uploadTime')}})},async uploadTestTime(){await uploadTestData({"data": uni.getStorageSync('uploadTestTime')}).then(res=>{if(res.data.code == 200 && res.data.status == 'success'){uni.removeStorageSync('uploadTestTime')}})},async uploadOpen(){this.openData = [];if(uni.getStorageSync('uploadOpen')){this.openData = uni.getStorageSync('uploadOpen')this.openData.push({"date": this.$utils.formatDateTime()})}else{this.openData[0] = {"date": this.$utils.formatDateTime()}}await uploadOpen({"data": this.openData}).then(res=>{if(res.data.code == 200 && res.data.status == 'success'){uni.removeStorageSync('uploadOpen')}else{uni.setStorageSync('uploadOpen',this.openData)}})}}}
</script><style>page{width: 100%;height: 100vh;}.progress-bar {background-color: #cccccc; /* 进度条背景颜色 */height: 10rpx;width: 60%;border-radius: 5rpx;}.progress-bar__fill {height: 100%;background-color: green; /* 完成部分的颜色 */transition: width 0.1s ease-in-out; /* 平滑过渡效果 */border-radius: 5rpx;}
</style>
有部分不需要的代码,可以自己删除掉即可。