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, 等后续有时间研究好了再完善这个权限配置。

相关推荐
蓝莓味的口香糖2 小时前
【npm】npm命令大全
前端·npm·node.js
我是天龙_绍2 小时前
uniapp一个关于自定义导航栏高度计算的问题
前端
**之火2 小时前
中止 Web 请求新方式 - AbortController API
开发语言·前端·javascript
我有一棵树2 小时前
如何优雅的布局,height: 100% 的使用和 flex-grow: 1 的 min-height 陷阱
前端·css·html
知识分享小能手3 小时前
微信小程序入门学习教程,从入门到精通,微信小程序页面交互 —— 知识点详解与案例实现(3)
前端·javascript·学习·react.js·微信小程序·小程序·交互
石小石Orz3 小时前
思考许久,我还是提交了离职申请
前端
m0_616188493 小时前
CSS中的伪类和伪元素
前端·javascript·css
一个小潘桃鸭3 小时前
组件抽离:el-upload支持图片粘贴上传并添加预览
前端
快乐就好ya3 小时前
React基础到进阶
前端·javascript·react.js