【问题标题】:Capacitor FileSystem API - Unable to write a file on device using Filesystem.writeFile电容器文件系统 API - 无法使用 Filesystem.writeFile 在设备上写入文件
【发布时间】:2020-07-06 12:57:47
【问题描述】:

我正在使用 Angular 9 / Ionic 5 / Capacitor 2.0 开发一个混合应用程序。我正在尝试将来自我的 API 的文件(基本上是 PDF 或图像文件)作为 Blob 保存到我的移动设备(IOS/Android)的文件系统中。我的最终目标是重现文件的类似浏览器的下载功能(允许用户将其保存在手机上)。

这是我的代码:

  downloadNativeFile(blob: Blob, fileName: string){
    let reader = this.getFileReader();
    reader.onloadend = (readerEvent) => {
      if(reader.error){
        console.log(reader.error);
      } else {
        let base64data: any = readerEvent.target['result'];
        try {
          Filesystem.writeFile({
            path: fileName,
            data: base64data,
            directory: FilesystemDirectory.Data
          }).then((res)=>{
            Filesystem.getUri({
              directory: FilesystemDirectory.Data,
              path: fileName
            }).then((getUriResult) => {
              const path = getUriResult.uri;
              console.log("The file's path : " + path);
              console.log(Capacitor.convertFileSrc(getUriResult.uri));              
            }, (error) => {
              console.log(error);
            });
          }).catch((err)=>{
            console.log("Error", err);
          });
        } catch(e) {
          console.error('Unable to write file', e);
        }
      }
    }

    reader.readAsDataURL(blob);
  }

执行此代码我没有收到任何错误,我的控制台会打印保存文件的路径:

The file's path : /DATA/sample.pdf
http://localhost/_capacitor_file_/DATA/sample.pdf

尽管如此,我在我的设备上找不到任何新文件(我还尝试了 FilesystemDirectory 枚举中的所有值)。在 Android (7 & 10) 和 IOS (13) 上测试。

我的 Blob 很好(我可以在浏览器中对其进行可视化),并且 dataUrl 转换已正确完成。

因此,Capacitor FileSystem API 中的 writeFile 函数似乎无法将文件保存在设备上。

有谁知道为什么以及如何解决这个问题?我真的被卡住了,目前无法实现此功能。

这是我的依赖项和插件:

  "dependencies": {
    "@angular/animations": "^9.1.7",
    "@angular/cdk": "^9.2.3",
    "@angular/common": "^9.1.7",
    "@angular/compiler": "^9.1.7",
    "@angular/core": "^9.1.7",
    "@angular/fire": "^5.4.2",
    "@angular/forms": "^9.1.7",
    "@angular/material": "^9.2.3",
    "@angular/material-moment-adapter": "^9.2.4",
    "@angular/platform-browser": "^9.1.7",
    "@angular/platform-browser-dynamic": "^9.1.7",
    "@angular/platform-server": "^9.1.7",
    "@angular/router": "^9.1.7",
    "@angular/service-worker": "^9.1.7",
    "@auth0/angular-jwt": "^2.1.0",
    "@capacitor/android": "^2.1.0",
    "@capacitor/core": "2.1.0",
    "@capacitor/ios": "^2.1.0",
    "@ionic-native/core": "^5.25.0",
    "@ionic-native/fcm": "^5.26.0",
    "@ionic-native/http": "^5.26.0",
    "@ionic-native/splash-screen": "^5.25.0",
    "@ionic-native/status-bar": "^5.25.0",
    "@ionic/angular": "^5.1.1",
    "@ngrx/effects": "^9.1.2",
    "@ngrx/entity": "^9.1.2",
    "@ngrx/store": "^9.1.2",
    "@ngrx/store-devtools": "^9.1.2",
    "ajv": "^6.9.1",
    "angular2-text-mask": "^9.0.0",
    "animate.css": "^3.7.0",
    "bootstrap-sass": "^3.4.0",
    "capacitor-fcm": "^2.0.0",
    "classlist.js": "^1.1.20150312",
    "cordova-plugin-advanced-http": "^2.4.1",
    "cordova-plugin-file": "^6.0.2",
    "core-js": "^2.6.4",
    "fibers": "^4.0.3",
    "file-saver": "^2.0.2",
    "firebase": "^5.8.2",
    "font-awesome": "^4.7.0",
    "html-webpack-plugin": "^2.21.0",
    "jetifier": "^1.6.6",
    "lottie-web": "^5.4.3",
    "moment": "^2.24.0",
    "ngx-markdown": "^9.0.0",
    "pwa": "^1.9.6",
    "rxjs": "^6.5.5",
    "scrolling-element": "^1.0.2",
    "tslib": "^1.10.0",
    "web-animations-js": "^2.3.2",
    "zone.js": "~0.10.2"
  },
  "devDependencies": {/**/},
  "cordova": {
    "plugins": {
      "cordova-plugin-advanced-http": {}
    }
  }

【问题讨论】:

  • 嗨 Ben,当你说“我在设备上找不到文件”时 - 你能解释一下你是如何检查的吗?基本上,本机文件 API 允许您使用沙盒文件系统,并且可以很好地阐明您是如何尝试检索文件的?
  • 嗨,谢尔盖,我正试图从我的 FileExplorer 中获取它。例如,Documents 目录应该是公共 Documents 文件夹,不是吗?我还尝试使用 FileOpener API 打开文件。我收到“找不到文件”错误
  • 实际上 writeFile 函数返回的 URI 看起来很奇怪:/DATA/sample.pdf 你不觉得吗?
  • 尝试使用:Capacitor.convertFileSrc(getUriResult.uri) 并将结果添加到您的问题中。另外,如果您可以添加说明您在哪个文件系统上测试(Android?)
  • 我刚刚编辑了帖子以添加您的要求。我在 Android 7 & 10、IOS 13 上测试过。我从 Capacitor.convertFileSrc(getUriResult.uri) 得到的路径是 localhost/_capacitor_file_/DATA/sample.pdf

标签: ionic-framework capacitor


【解决方案1】:

console.log 方法的输出实际上可以提示文件系统导入错误。

代替:

import { Filesystem } from '@capacitor/core';

试试:

const { Filesystem } = Plugins;

【讨论】:

  • 你拯救了我的一天!我不明白为什么 Filesystem 不能在真实设备上运行。
  • 非常感谢!重构后我有一个损坏的服务,但几乎不可能发现,因为它不会抛出任何错误,而是回退到浏览器 indexeddb API ...
  • @Adam Zabrocki 读取文本文件时返回文件 0。
【解决方案2】:

文件系统访问很棘手,因为每个系统(Android、iOS 等)都有其own rules

Android 允许您描述的行为,但不是 FilesystemDirectory.Data 作为目录来写入您需要使用 FilesystemDirectory.Documents 的文件。在此处查看更多信息:https://capacitorjs.com/docs/apis/filesystem(滚动到 FilesystemDirectory 枚举)

iOS 不允许直接访问文件,您可以尝试使用 icloud 文件夹路由来实现您想要的目标。看到这个答案:Save file to public directory using Cordova FileTransfer

但是由于您的用例是允许类似“下载”的行为 - 尝试看看这个库是否可以为您解决问题:https://github.com/eligrey/FileSaver.js/ 它是 Web API 而非原生 API,但在现代浏览器中运行良好。

【讨论】:

    【解决方案3】:

    先生您好,先生,我有同样的问题,我想在我的代码上实现这个。先生,我可以看看这个的完整代码吗?

    let reader = this.getFileReader();
    reader.onloadend = readerEvent => {
      if (reader.error) {
         console.error(reader.error);
      } else {
         let base64data: any = readerEvent.target['result'];
         let mimeType = base64data.substring(base64data.indexOf(":")+1, base64data.indexOf(";"));
         let options: IWriteOptions = { replace: true };
         this.file.writeFile(this.file.dataDirectory, fileName, blob, options).then(res => {
            this.fileOpener.open(this.file.dataDirectory + fileName, mimeType);
         });
      }
    };
    reader.readAsDataURL(res.body);
    
    

    尤其是这个 let reader = this.getFileReader(); 以及您使用的导入包,谢谢

    【讨论】:

      【解决方案4】:

      我终于找到了另一种方法,所以如果它可以帮助别人,我会发布它。总而言之,其目的是能够将来自 API 的文件作为 Blob 下载/显示在移动设备上。

      这是我的工作代码:

      let reader = this.getFileReader();
      reader.onloadend = readerEvent => {
        if (reader.error) {
           console.error(reader.error);
        } else {
           let base64data: any = readerEvent.target['result'];
           let mimeType = base64data.substring(base64data.indexOf(":")+1, base64data.indexOf(";"));
           let options: IWriteOptions = { replace: true };
           this.file.writeFile(this.file.dataDirectory, fileName, blob, options).then(res => {
              this.fileOpener.open(this.file.dataDirectory + fileName, mimeType);
           });
        }
      };
      reader.readAsDataURL(res.body);
      

      我终于把FileSystem API的用法换成了File API。 FileOpener 允许使用适当的应用程序打开文件,具体取决于其 Mime-Type。它在我测试的所有设备(Android 7 & 10 和 Ios 13)上都能完美运行

      【讨论】:

      • 您好先生,先生,我遇到了同样的问题,我想在我的代码上实现这个。先生,我可以看到完整的代码吗?尤其是这个``` let reader = this.getFileReader(); ``` 以及您使用的导入包,谢谢
      猜你喜欢
      • 2022-10-18
      • 1970-01-01
      • 1970-01-01
      • 2023-03-31
      • 2018-06-27
      • 1970-01-01
      • 2015-04-09
      • 2017-12-16
      • 2011-12-14
      相关资源
      最近更新 更多