Uniapp IOS 和 Android 下的文件写入用户目录

最近需要在 APP 中实现文件的导出功能,研究了有一会的将文件保存到用户目录,并打开文件预览。为了防止后续再次踩坑,所以快速记录一下配置方式。

文件读写授权

在 uniapp 中要首先 IOS 和 Android 将文件保存到用户本地目录, 首先需要在 manifest.json 配置文件读写权限, 如果不配置读写权限则会保存到 Android 的包名路径/ IOS 容器路径中

json 复制代码
      "android": {
        "permissions": [
          "<uses-permission android:name=\"android.permission.WRITE_EXTERNAL_STORAGE\"/>",
          "<uses-permission android:name=\"android.permission.READ_EXTERNAL_STORAGE\"/>"
         ]
      }
      "apple": {
        "plistcmds": [
          "Set :NSFileProviderDomainUsageDescription 需要访问文件以保存用户数据",
          "Set :UIFileSharingEnabled true",
          "Set :LSSupportsOpeningDocumentsInPlace true"
        ]
      }

在 Android 下, 需要明确指定以 file://storage/emulated/0/ 为开头的文件路径,才能保存到本地目录。而在 IOS 中, 可以直接指定 _doc 目录

文件下载保存并打开

为了放置文件重复保存, 建议先使用 resolveLocalFileSystemURL 首先判断一下文件是否下载过了, 如果文件已经存在, 那么就直接执行例如文件打开的操作

如果文件不存在, 就使用 plus.downloader.createDownload 将文件下载到指定的目录,再使用 plus.io.convertLocalFileSystemURL 将下载文件转为平台路径,最后在执行例如文件打开的操作

javascript 复制代码
/**
 * 保存并打开文件
 * @param url 文件网络地址
 */
async function saveFile(url: string) {
  const fileName = url.split("/").pop()?.split("?")[0];

  console.log(url);
  // #ifdef APP-PLUS
  let realAbs = "";
  const platform = uni.getSystemInfoSync().platform;
  if (platform === "ios") {
    realAbs = "_doc";
  }

  if (platform === "android") {
    let granted = await permision.requestAndroidPermission(
      "android.permission.WRITE_EXTERNAL_STORAGE"
    );
    if (granted === GrantedStatusEnum.GRANTED) {
      realAbs = "file://storage/emulated/0/Download";
    } else {
      // 文件保存到 /Download
      realAbs = "_downloads";
      notify.showNotify({
        message: `未能授权文件权限,文件将临时保存`,
        type: "danger",
      });
    }
  }

  // 平台路径
  const relPath = `${realAbs}/${fileName}`;

  // 文件判存
  plus.io.resolveLocalFileSystemURL(
    relPath,
    async () => {
      console.log("[saveFile] 文件已存在,直接打开");

      if (platform === "android") {
        await uni.showToast({
          title: `文件已保存至: /Download/${fileName}`,
          icon: "none",
        });
      }

      openDocument(relPath);
    },
    () => {
      console.log("[saveFile] 文件不存在,开始下载");
      const task = plus.downloader.createDownload(
        url,
        { filename: relPath },
        async (d, status) => {
          if (status === 200) {
            if (platform === "android") {
              await uni.showToast({
                title: `文件已保存至: /Download/${fileName}`,
                icon: "none",
              });
            }

            const realAbs = plus.io.convertLocalFileSystemURL(d.filename);
            openDocument(realAbs);
          } else {
            notify.showNotify({
              message: `文件导出失败, 请稍后重试`,
              type: "danger",
            });
          }
        }
      );
      task.start();
    }
  );
  // #endif

  // #ifdef H5
  location.href = url;
  // #endif
}

function openDocument(absPath: string) {
  plus.runtime.openFile(absPath, {}, (res) => {
    if (res.code === -1) {
      notify.showNotify({
        message: "无法打开文件,请检查是否安装对应应用",
        type: "danger",
      });
    }
  });
}

结束语

目前 IOS 的保存路径还没弄清楚,现在 IOS 需要文件预览打开后手动保存一次。指定 _downloads 似乎没有生效,可能需要手动请求一次权限才能完成写入,所以这里先投机取巧写入了 _doc, 等后续有时间研究好了再完善这个权限配置。

相关推荐
崔庆才丨静觅7 小时前
hCaptcha 验证码图像识别 API 对接教程
前端
passerby60617 小时前
完成前端时间处理的另一块版图
前端·github·web components
掘了7 小时前
「2025 年终总结」在所有失去的人中,我最怀念我自己
前端·后端·年终总结
崔庆才丨静觅7 小时前
实用免费的 Short URL 短链接 API 对接说明
前端
崔庆才丨静觅8 小时前
5分钟快速搭建 AI 平台并用它赚钱!
前端
崔庆才丨静觅8 小时前
比官方便宜一半以上!Midjourney API 申请及使用
前端
Moment8 小时前
富文本编辑器在 AI 时代为什么这么受欢迎
前端·javascript·后端
崔庆才丨静觅9 小时前
刷屏全网的“nano-banana”API接入指南!0.1元/张量产高清创意图,开发者必藏
前端
剪刀石头布啊9 小时前
jwt介绍
前端
爱敲代码的小鱼9 小时前
AJAX(异步交互的技术来实现从服务端中获取数据):
前端·javascript·ajax