uni-app多端资源文件下载实现方案

一、背景介绍

在开发 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:条件编译结束标志 这样做的原因:
  1. API差异:不同平台提供的API不同
  2. 运行环境差异:H5运行在浏览器中,而小程序和App运行在各自的容器中
  3. 安全限制差异:不同平台有不同的安全策略
  4. 用户体验优化:可以针对不同平台提供最优的实现方案

五、实现细节解析

5.1 H5端实现要点

  1. 使用 XMLHttpRequest 请求文件数据
  2. 设置 responseType 为 arraybuffer
  3. 使用 Blob 创建临时URL
  4. 通过动态创建 <a /> 标签元素触发下载
  5. 下载完成后清理临时元素

5.2 小程序和App实现要点

  1. 使用 uni.downloadFile 下载文件
  2. 使用 uni.saveVideoToPhotosAlbumuni.saveImageToPhotosAlbum保存文件
  3. 实现下载进度监听
  4. 完善的状态管理和错误处理
  5. 注意权限申请

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 提示
  • 成功/失败的反馈

七、最佳实践建议

  1. 封装统一的下载方法,内部根据平台调用不同实现
  2. 添加完善的错误处理和状态管理
  3. 实现下载进度提示
  4. 注意内存管理,及时清理临时文件
  5. 考虑网络状态和存储空间检查

八、总结

条件编译实现多端文件下载是一个很好的跨平台解决方案。它让我们能够:

  1. 充分利用各平台特性
  2. 提供最佳的用户体验
  3. 保持代码的可维护性
  4. 实现统一的业务逻辑

这篇文章能帮助你更好地理解和实现uni-app多端文件下载功能!

相关推荐
北城以北888843 分钟前
Vue--Vue基础(一)
前端·javascript·vue.js
sniper_fandc2 小时前
Vue Router路由
前端·javascript·vue.js
Roye_ack13 小时前
【项目实战 Day9】springboot + vue 苍穹外卖系统(用户端订单模块 + 商家端订单管理模块 完结)
java·vue.js·spring boot·后端·mybatis
2501_9159184115 小时前
iOS 框架全解析,原生框架与跨平台框架对比、开发应用打包与 App Store 上架实战经验
android·ios·小程序·https·uni-app·iphone·webview
北城以北888816 小时前
Vue--Vue基础(二)
前端·javascript·vue.js
FuckPatience17 小时前
Vue 组件定义模板,集合v-for生成界面
前端·javascript·vue.js
街尾杂货店&17 小时前
cssword属性
uni-app
有梦想的攻城狮18 小时前
从0开始学vue:vue和react的比较
前端·vue.js·react.js
鱼樱前端20 小时前
uni-app快速入门章法(二)
前端·uni-app
silent_missile20 小时前
vue3父组件和子组件之间传递数据
前端·javascript·vue.js