概览

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.savedFilePath
								if (inta < data.length - 1) {
									inta = inta + 1
									downloadClass(data, inta)
								} else {
									uni.setStorageSync('classData', data)
								}
							},
							fail: err => {
								if (inta < data.length - 1) {
									inta = inta + 1
									downloadClass(data, inta)
								} else {
									uni.setStorageSync('classData', data)
								}
							}
						});
					} else {
						if (inta < data.length - 1) {
							inta = inta + 1
							downloadClass(data, inta)
						} else {
							uni.setStorageSync('classData', data)
						}
					}
				},
				fail: err => {
					if (inta < data.length - 1) {
						inta = inta + 1
						downloadClass(data, inta)
					} else {
						uni.setStorageSync('classData', data)
					}
				}
			});
			downloadTask.onProgressUpdate(resda => {})
		} catch (e) {
			if (inta < data.length - 1) {
				inta = inta + 1
				downloadClass(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.savedFilePath
								if (inta < data.length - 1) {
									inta = inta + 1
									downloadSubject(data, inta, callBackFun)
								} else {
									uni.setStorageSync('SubjectData', data, callBackFun)
									requestSubjectClass(data, 0, callBackFun)
								}
							},
							fail: err => {
								if (inta < data.length - 1) {
									inta = inta + 1
									downloadSubject(data, inta, callBackFun)
								} else {
									uni.setStorageSync('SubjectData', data)
									requestSubjectClass(data, 0, callBackFun)
								}
							}
						});
					} else {
						if (inta < data.length - 1) {
							inta = inta + 1
							downloadSubject(data, inta, callBackFun)
						} else {
							uni.setStorageSync('SubjectData', data)
							requestSubjectClass(data, 0, callBackFun)
						}
					}
				},
				fail: err => {
					if (inta < data.length - 1) {
						inta = inta + 1
						downloadSubject(data, inta, callBackFun)
					} else {
						uni.setStorageSync('SubjectData', data)
						requestSubjectClass(data, 0, callBackFun)
					}
				}
			});
			downloadTask.onProgressUpdate(resda => {
				callBackFun(resda.progress, false)
			})
		} catch (e) {
			//TODO handle the exception
			if (inta < data.length - 1) {
				inta = inta + 1
				downloadSubject(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.list
					if (inta < data.length - 1) {
						inta = inta + 1
						requestSubjectClass(data, inta, callBackFun)
					} else {
						uni.setStorageSync('SubjectData', data)
						downloadSubjectFile(data, 0, 0, callBackFun)
					}
				} else {
					if (inta < data.length - 1) {
						inta = inta + 1
						requestSubjectClass(data, inta, callBackFun)
					} else {
						uni.setStorageSync('SubjectData', data)
						downloadSubjectFile(data, 0, 0, callBackFun)
					}
				}
			})
		} catch (e) {
			//TODO handle the exception
			if (inta < data.length - 1) {
				inta = inta + 1
				requestSubjectClass(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.savedFilePath
									if (intaClass < data[inta].categoryData.length - 1) {
										intaClass = intaClass + 1
										downloadSubjectFile(data, inta, intaClass,callBackFun)
									} else {
										if (inta < data.length - 1) {
											inta = inta + 1
											downloadSubjectFile(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 + 1
										downloadSubjectFile(data, inta, intaClass,
											callBackFun)
									} else {
										if (inta < data.length - 1) {
											inta = inta + 1
											downloadSubjectFile(data, inta, 0, callBackFun)
										} else {
											uni.setStorageSync('SubjectData', data)
											callBackFun(100, true)
										}
									}
								}
							});
						} else {
							if (intaClass < data[inta].categoryData.length - 1) {
								intaClass = intaClass + 1
								downloadSubjectFile(data, inta, intaClass, callBackFun)
							} else {
								if (inta < data.length - 1) {
									inta = inta + 1
									downloadSubjectFile(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 + 1
							downloadSubjectFile(data, inta, intaClass, callBackFun)
						} else {
							if (inta < data.length - 1) {
								inta = inta + 1
								downloadSubjectFile(data, inta, 0, callBackFun)
							} else {
								uni.setStorageSync('SubjectData', data)
								callBackFun(100, true)
							}
						}
					}
				});
				downloadTask.onProgressUpdate(resda => {
					callBackFun(resda.progress, false)
				})
			} catch (e) {
				//TODO handle the exception
				console.log("downloadSubjectFile  catch  " + JSON.stringify(e))
				if (intaClass < data[inta].categoryData.length - 1) {
					intaClass = intaClass + 1
					downloadSubjectFile(data, inta, intaClass, callBackFun)
				} else {
					if (inta < data.length - 1) {
						inta = inta + 1
						downloadSubjectFile(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 + 1
				downloadSubjectFile(data, inta, intaClass, callBackFun)
			} else {
				if (inta < data.length - 1) {
					inta = inta + 1
					downloadSubjectFile(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.list
					if (inta < data.length - 1) {
						inta = inta + 1
						requestSubjectClassSize(data, inta, callBack)
					} else {
						requestDownloadFileSize(data, 0, 0,0, callBack)
					}
				} else {
					if (inta < data.length - 1) {
						inta = inta + 1
						requestSubjectClassSize(data, inta, callBack)
					} else {
						requestDownloadFileSize(data, 0, 0,0, callBack)
					}
				}
			})
		} catch (e) {
			//TODO handle the exception
			if (inta < data.length - 1) {
				inta = inta + 1
				requestSubjectClassSize(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 + 1
								if(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 + 1
									if(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 + 1
								requestDownloadFileSize(data, inta, intaClass,contentLength, callBack)
							} else {
								if (inta < data.length - 1) {
									inta = inta + 1
									requestDownloadFileSize(data, inta, 0,contentLength, callBack)
								} else {
									callBack(contentLength)
								}
							}
						}
					},
					error(err) {
						console.error('请求错误:', err);
						if (intaClass < data[inta].categoryData.length - 1) {
							intaClass = intaClass + 1
							requestDownloadFileSize(data, inta, intaClass,contentLength, callBack)
						} else {
							if (inta < data.length - 1) {
								inta = inta + 1
								requestDownloadFileSize(data, inta, 0,contentLength, callBack)
							} else {
								callBack(contentLength)
							}
						}
					},
					networkTimeout: 5000, // 设置超时时间
					timeout: 5000 // 设置整体请求超时时间
				})
			} catch (e) {
				//TODO handle the exception
				console.log("requestDownloadFileSize  catch  " + JSON.stringify(e))
				if (intaClass < data[inta].categoryData.length - 1) {
					intaClass = intaClass + 1
					requestDownloadFileSize(data, inta, intaClass,contentLength, callBack)
				} else {
					if (inta < data.length - 1) {
						inta = inta + 1
						requestDownloadFileSize(data, inta, 0,contentLength, callBack)
					} else {
						callBack(contentLength)
					}
				}
			}
		} else {
			if (intaClass < data[inta].categoryData.length - 1) {
				intaClass = intaClass + 1
				requestDownloadFileSize(data, inta, intaClass,contentLength, callBack)
			} else {
				if (inta < data.length - 1) {
					inta = inta + 1
					requestDownloadFileSize(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 + 1
							if(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 + 1
							requestFileSize(data, inta, contentLength, callBack)
						} else {
							callBack(contentLength)
						}
					}
				},
				error(err) {
					console.error('请求错误:', err);
					if (inta < data.length - 1) {
						inta = inta + 1
						requestFileSize(data, inta, contentLength, callBack)
					} else {
						callBack(contentLength)
					}
				},
				networkTimeout: 5000, // 设置超时时间
				timeout: 5000 // 设置整体请求超时时间
			});
		} catch (e) {
			//TODO handle the exception
			if (inta < data.length - 1) {
				inta = inta + 1
				requestFileSize(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 = this
				await 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 = this
				await 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>

有部分不需要的代码,可以自己删除掉即可。

10-27 10:02