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

相关推荐
阿奇__12 小时前
uniapp微信小程序图片上传含回显组件(拍照,相册)
前端·javascript·vue.js
2501_9159090613 小时前
IPA 深度混淆是什么意思?从混淆强度到实际效果的解读
android·ios·小程序·https·uni-app·iphone·webview
前端甜糖13 小时前
前端如何实现在线预览office常见文件功能?
前端·vue.js
ljs64827395115 小时前
智慧商城(Smart Mall):基于 Spring Boot + Vue 3 的现代化电商平台实战
linux·vue.js·spring boot·后端
蜡台15 小时前
修改Uniapp input 组件不刷新视图问题
前端·uni-app·input
析数塔15 小时前
2026前端框架深度解析:React Compiler、Angular Zoneless、Vue 3.5重写游戏规则
前端·vue.js·react.js
paopaokaka_luck1 天前
英语单词学习系统的设计与实现(基于艾宾浩斯遗忘曲线的智能复习机制、语音朗读、多题型测试与错题自动收录、Echarts图形化分析)
vue.js·spring boot·后端·echarts
Momo__1 天前
Vite 8.1 打包开发模式——10000 组件冷启动 15 倍,"No Bundle Dev" 七年后被自己终结
前端·vue.js·vite
HUMHSX1 天前
Vue 组件通信核心知识点梳理
javascript·vue.js·ecmascript
妙码生花1 天前
从 PHP 到 AI + Golang,程序员自救转型手记(二十六):icon 组件封装
前端·vue.js·typescript