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.saveVideoToPhotosAlbum 或uni.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多端文件下载功能!

相关推荐
码艺-Alimjan3 小时前
Tauri 源码备份 · 打包 & 解压 总结,源码收集之路
前端·javascript·vue.js
顽疲6 小时前
Java vue 养老系统源码详解:月账单 care_month_bill 与 Quartz 扣费 Job 实践
java·开发语言·vue.js·vue养老·springboot养老·java养老
毕业设计7037 小时前
(免费领源码)基于node的学生公寓管理系统06412- java、PHP、python、C#、小程序、大数据、单片机、网络工程等)
vue.js·mysql·node.js·maven·mybatis·express·b/s
古夕8 小时前
地址变了,页面是空的:一次 Vue Router 菜单切换复盘
前端·vue.js
沙漏无语12 小时前
pnpm 安装@vue-office包的幽灵依赖问题解决
前端·javascript·vue.js
雪芽蓝域zzs12 小时前
第 9 节:面积测量(测面工具)
vue.js
℘团子এ12 小时前
vue2中自定义指令的使用
前端·javascript·vue.js
℘团子এ14 小时前
vue3中自定义指令的使用
前端·javascript·vue.js
m0_5873830016 小时前
碰碰好评卡功能落地实战 从需求梳理到上线部署全流程实操指南
java·linux·服务器·前端·javascript·vue.js·人工智能
Pu_Nine_916 小时前
Vue‑Flow 从零入门实战|AI‑Agent 拓扑图开发
前端·javascript·vue.js