一、背景介绍
在开发 uni-app 应用时,经常需要实现文件下载功能。但由于不同平台(H5、小程序、App)的运行环境和API差异,我们需要采用条件编译来实现跨平台的文件下载功能。
二、平台差异分析
2.1 H5平台特点
-
运行在浏览器环境中
-
可以使用浏览器原生的 XMLHttpRequest 和 Blob API
-
通过创建临时
<a />
标签下载 -
受同源策略限制,需要注意跨域问题
2.2 小程序和App平台特点
- 运行在原生环境中
- 使用平台提供的专有API
- 需要申请相关权限
- 可以监听下载进度
- 下载完成后需要调用特定API保存到相册
三、实现方案
3.1 H5端实现
js
async function saveFileToLocal(fileUrl) {
// #ifdef H5
uni.showLoading({
title: '正在下载中...',
mask: true
})
const xhr = new XMLHttpRequest();
xhr.open('GET', fileUrl, true);
xhr.responseType = 'arraybuffer'; // 返回类型blob
xhr.onload = function () {
if (xhr.readyState === 4 && xhr.status === 200) {
let blob = this.response;
// 转换为blob链接
let url = window.URL.createObjectURL(
new Blob([blob], {
type: 'video/mp4' // 根据文件类型设置
})
)
// 创建临时下载链接
let a = document.createElement('a');
a.download = 'filename'; // 设置下载文件名
a.href = url;
a.style.display = 'none';
document.body.appendChild(a);
a.click();
uni.hideLoading();
document.body.removeChild(a);
}
};
xhr.send();
// #endif
}
3.2 小程序和App实现
js
async function saveFileToLocal(fileUrl) {
// #ifndef H5
return new Promise((resolve, reject) => {
// 下载状态控制
isDownloading.value = true;
downloadProgress.value = 0;
const downloadTask = uni.downloadFile({
url: fileUrl,
success: (res) => {
if (res.statusCode === 200) {
// 下载成功后保存到相册
uni.saveVideoToPhotosAlbum({
filePath: res.tempFilePath,
success: () => {
uni.showToast({
title: '保存成功',
icon: 'success'
});
resolve('保存成功');
},
fail: (err) => {
uni.showToast({
title: '保存失败',
icon: 'none'
});
reject(err);
}
});
}
},
fail: (err) => {
uni.showToast({
title: '下载失败',
icon: 'none'
});
reject(err);
},
complete: () => {
isDownloading.value = false;
downloadProgress.value = 0;
}
});
// 监听下载进度
downloadTask.onProgressUpdate((res) => {
downloadProgress.value = res.progress;
});
});
// #endif
}
四、条件编译说明
uni-app 使用条件编译来处理平台差异:
- // #ifdef H5:仅在 H5 平台编译
- // #ifndef H5:除了 H5 平台都编译
- // #endif:条件编译结束标志 这样做的原因:
- API差异:不同平台提供的API不同
- 运行环境差异:H5运行在浏览器中,而小程序和App运行在各自的容器中
- 安全限制差异:不同平台有不同的安全策略
- 用户体验优化:可以针对不同平台提供最优的实现方案
五、实现细节解析
5.1 H5端实现要点
- 使用 XMLHttpRequest 请求文件数据
- 设置 responseType 为 arraybuffer
- 使用 Blob 创建临时URL
- 通过动态创建
<a />
标签元素触发下载 - 下载完成后清理临时元素
5.2 小程序和App实现要点
- 使用 uni.downloadFile 下载文件
- 使用 uni.saveVideoToPhotosAlbum 或uni.saveImageToPhotosAlbum保存文件
- 实现下载进度监听
- 完善的状态管理和错误处理
- 注意权限申请
5.3 权限说明
1.Android权限解释:
- WRITE_EXTERNAL_STORAGE: 写入外部存储权限
- READ_EXTERNAL_STORAGE: 读取外部存储权限
- INTERNET: 网络访问权限
- MOUNT_UNMOUNT_FILESYSTEMS: 挂载文件系统权限
- MANAGE_EXTERNAL_STORAGE: Android 10及以上的存储权限
- MEDIA_CONTENT_CONTROL: 媒体文件控制权限
六、注意事项
1.权限处理
- 小程序需要在 manifest.json 中声明相关权限
- App 需要动态申请存储权限
- H5 需要注意跨域问题
2.文件类型
- 图片:image/jpeg、image/png 等
- 视频:video/mp4 等
- 需要根据实际文件类型设置正确的 MIME type
3.错误处理
- 网络错误
- 权限拒绝
- 存储空间不足
- 文件格式不支持
4.用户体验
- 添加下载进度提示
- 适当的 loading 提示
- 成功/失败的反馈
七、最佳实践建议
- 封装统一的下载方法,内部根据平台调用不同实现
- 添加完善的错误处理和状态管理
- 实现下载进度提示
- 注意内存管理,及时清理临时文件
- 考虑网络状态和存储空间检查
八、总结
条件编译实现多端文件下载是一个很好的跨平台解决方案。它让我们能够:
- 充分利用各平台特性
- 提供最佳的用户体验
- 保持代码的可维护性
- 实现统一的业务逻辑
这篇文章能帮助你更好地理解和实现uni-app多端文件下载功能!