H5 WebView 文件下载到手机中(仅安卓与 iOS)

H5 WebView 文件下载(仅安卓与 iOS)

原理

  • 使用 H5+ 原生接口 plus.downloader.createDownload 将文件下载到本地;下载完成后通过 plus.runtime.openFile 打开。
  • 不同平台保存路径不同:
    • Android:保存到系统公共 Download 目录(需要存储权限)。
    • iOS:保存到应用沙箱 _doc 持久目录(无需额外权限)。

使用方法(代码)

  • 页面按钮"原生下载PDF"触发函数:src/views/w-success.vue:323
  • 关键实现(节选):
ts 复制代码
// src/views/w-success.vue:323-388
const downloadByPlus = async () => {
  const p = (window as any).plus;
  if (!p) { alert("当前不在App环境"); return; }
  if (isDownloading.value) return;
  isDownloading.value = true;
  downloadStatus.value = "原生下载中...";

  try {
    const fileOnly = getFileName(downloadUrl);
    const isAndroid = p.os && p.os.name === "Android";
    const isiOS = p.os && p.os.name === "iOS";
    const filename = isAndroid
      ? `file:///storage/emulated/0/Download/${fileOnly}`
      : isiOS
      ? `_doc/${fileOnly}`
      : `_downloads/${fileOnly}`;

    const startDownload = () =>
      p.downloader.createDownload(
        downloadUrl,
        { filename },
        (d: any, status: number) => {
          isDownloading.value = false;
          if (status === 200) {
            let localPath = "";
            try { localPath = p.io.convertLocalFileSystemURL(d.filename); } catch (err) { localPath = ""; }
            downloadStatus.value = localPath ? `下载完成,路径:${localPath}` : "下载完成";
            try { p.runtime.openFile(d.filename); } catch (e: any) { downloadStatus.value = `${downloadStatus.value},打开失败: ${e?.message || e}`; }
          } else {
            downloadStatus.value = `下载失败,状态码:${status}`;
          }
        }
      ).start();

    if (isAndroid) {
      try {
        await new Promise((resolve, reject) => {
          p.android.requestPermissions(
            ["android.permission.WRITE_EXTERNAL_STORAGE", "android.permission.READ_EXTERNAL_STORAGE"],
            () => resolve(null),
            (err: any) => reject(err)
          );
        });
      } catch (err: any) {
        isDownloading.value = false;
        downloadStatus.value = `缺少存储权限: ${err?.message || err}`;
        return;
      }
    }
    startDownload();
  } catch (e: any) {
    isDownloading.value = false;
    downloadStatus.value = `原生下载失败: ${e?.message || e}`;
  }
};

保存路径

  • Android:/storage/emulated/0/Download/<文件名>(绝对路径显示为 file:///storage/emulated/0/Download/<文件名>)。
  • iOS:_doc/<文件名>(绝对路径通过 plus.io.convertLocalFileSystemURL 转换后显示为 file:///.../Documents/<文件名> 等沙箱路径)。

平台要求

  • Android:需要申请 WRITE_EXTERNAL_STORAGEREAD_EXTERNAL_STORAGE 权限;代码中已请求(src/views/w-success.vue:356-371)。
  • iOS:若下载地址为 http://,需在原生打包的 ATS 配置中允许该域名的非 HTTPS 访问;文件仅可写入应用沙箱。
相关推荐
黑色的糖果2 小时前
vue中tailwindcss插件的引入及使用
前端·javascript·vue.js
戌中横2 小时前
JavaScript——预解析
前端·javascript·学习
木斯佳3 小时前
前端八股文面经大全:26届秋招滴滴校招前端一面面经-事件循环题解析
前端·状态模式
光影少年4 小时前
react状态管理都有哪些及优缺点和应用场景
前端·react.js·前端框架
saber_andlibert5 小时前
TCMalloc底层实现
java·前端·网络
逍遥德5 小时前
如何学编程之01.理论篇.如何通过阅读代码来提高自己的编程能力?
前端·后端·程序人生·重构·软件构建·代码规范
冻感糕人~5 小时前
【珍藏必备】ReAct框架实战指南:从零开始构建AI智能体,让大模型学会思考与行动
java·前端·人工智能·react.js·大模型·就业·大模型学习
程序员agions5 小时前
2026年,“配置工程师“终于死绝了
前端·程序人生
alice--小文子6 小时前
cursor-mcp工具使用
java·服务器·前端
晚霞的不甘6 小时前
揭秘 CANN 内存管理:如何让大模型在小设备上“轻装上阵”?
前端·数据库·经验分享·flutter·3d