capacitor icon indicating copy to clipboard operation
capacitor copied to clipboard

bug: convertFileSrc Does Not Work in Web

Open seanwu1105 opened this issue 5 years ago • 12 comments

Bug Report

Capacitor Version

Latest Dependencies:

  @capacitor/cli: 2.4.0
  @capacitor/core: 2.4.0
  @capacitor/android: 2.4.0
  @capacitor/electron: 2.4.0
  @capacitor/ios: 2.4.0

Installed Dependencies:

  @capacitor/android not installed
  @capacitor/ios not installed
  @capacitor/cli 2.4.0
  @capacitor/core 2.4.0
  @capacitor/electron not installed

Platform(s)

Web.

Current Behavior

I followed the sample in the documentation. However, the converted path does not work on the browser showing GET http://localhost:8100/DATA/demo 404 (Not Found).

Expected Behavior

The image should be loaded with the provided path.

Code Reproduction

The minimal sample Ionic project can be found in this repository.

A tiny image is hard-coded as base64 string in the data field when writing the file with Capacitor Filesystem API.

const writeResult = await Filesystem.writeFile({
  data: 'iVBORw0KGgoAAAANSUhEUgAAAAYAAAAECAYAAACtBE5DAAAABHNCSVQICAgIfAhkiAAAABl0RVh0U29mdHdhcmUAZ25vbWUtc2NyZWVuc2hvdO8Dvz4AAAA9aVRYdENyZWF0aW9uIFRpbWUAAAAAADIwMjDlubTlhavmnIgzMeaXpSAo6YCx5LiAKSAyM+aZgjI35YiGMjTnp5IACi9UAAAASElEQVQImWOUk5P7z4AFsEAoSQa/kGYGu2ezGbazuzP4K8jBJBgYGBjYGETVwxiszi9gaDp0A1mCgYHh7U6G6RcuM7xhYGAAAEFTDx/VUsAUAAAAAElFTkSuQmCC',
  directory: FilesystemDirectory.Data,
  path: 'demo',
  recursive: true
});
this.src = Capacitor.convertFileSrc(writeResult.uri);
const readResult = await Filesystem.readFile({
  directory: FilesystemDirectory.Data,
  path: 'demo'
});
this.base64Src = `data:image/png;base64,${readResult.data}`;

The template shows two identical images with different approach: by path and by base64 string.

<img [src]="src" />
<img [src]="base64Src" />

The first image throws a 404 error.

Other Technical Details

npm --version output: 6.14.8

node --version output: v10.16.0

Other dependencies can be found in the sample project.

seanwu1105 avatar Aug 31 '20 16:08 seanwu1105

This example may not translate well to web. The Capacitor FS plugin uses IndexedDB for the web implementation, so there aren't any actual file paths to convert.

imhoffd avatar Sep 15 '20 23:09 imhoffd

@dwieeb The documentation needs to be updated to reflect this limitation. Also, the implementation should throw an error instead of return the same value.

https://github.com/ionic-team/capacitor/blob/d7bf775dfb3857e5fc0edb2b24cbe32f56b821b0/core/src/web-runtime.ts#L64-L66

seanwu1105 avatar Sep 18 '20 07:09 seanwu1105

It works if you use DOMSanitizer, like this: this.sanitizer.bypassSecurityTrustUrl(Capacitor.convertFileSrc(URI)) Sanitizer should be injected in constructor.

Gorshtak avatar Nov 04 '20 13:11 Gorshtak

@Gorshtak Thanks for the suggestion. However, it does not work after I apply DOMSanitizer to the URL.

this.sanitizedSrc = this.sanitizer.bypassSecurityTrustUrl(Capacitor.convertFileSrc(writeResult.uri));

You can find the sample project here.

seanwu1105 avatar Nov 05 '20 06:11 seanwu1105

Oh sorry, i just noticed that you said it doesn't work on web. Well FileSystem is a plugin that provides manipulation of files on native devices. I don't see why would you use it on web.

Gorshtak avatar Nov 05 '20 14:11 Gorshtak

@Gorshtak I try to build apps with Capacitor on native devices. However, it is still convenient for me to fast prototype the look and feel on the browser with ionic serve before applying to the native environment. Furthermore, the official documentation says the Filesystem plugin supports Android, iOS and PWA, which is misleading as the WriteResult.uri is not usable on PWA. This should be at least documented.

seanwu1105 avatar Nov 05 '20 15:11 seanwu1105

Well if you go a little bit deeper into the documentation inside FIleSystem you can see that they don't mention web anywhere. For example look at this: Screenshot 2020-11-05 at 16 23 34 There is no explanation what it does on WEB.

But i completely agree with you that it should be documented that FileSystem does not support WEB

Gorshtak avatar Nov 05 '20 15:11 Gorshtak

@Gorshtak The document has the icon for PWA support:

Screenshot from 2020-11-05 23-38-47

seanwu1105 avatar Nov 05 '20 15:11 seanwu1105

I had similar problem, on android 10 only

so basically for angular:

import { WebView } from '@ionic-native/ionic-webview/ngx';
constructor(private webView: WebView) {}
...
this.webView.convertFileSrc(path)

or you can also follow official ionic way https://forum.ionicframework.com/t/how-to-use-capacitors-convertfilesrc-instead-of-cordovas-convertfilesrc/167283

you dont need to do "http://localhost/" + relative path(i saw on different forums this way of solving), the problem is that if you have live-reloading enabled, convertFileSrc will convert your path to "http://192.168..../" (external adress). If You disable live-reloading, and remove "server": "{...}" from your capacitor.config.json your convertFileSrc will convert path to "http://localhost/..."

but still, all works fine on 7, 8, 9, 11 android. 10 still throws 404(OK) and if i try to send to backend images that was parsed with this error i will get error in console: EACCESS (Permission Denied)

Solved this by adding ' android:requestLegacyExternalStorage="true" ' in AndroidManifest.xml in tag

ghost avatar Dec 12 '20 13:12 ghost

If you deploy into a web app this function isn't going to work. As they said about convertfilesrc : "Convert a device filepath into a Web View-friendly path." (https://capacitorjs.com/docs/basics/utilities#convertfilesrc).

As I understood, on a web context (ionic serve) the file isn't "exist", it is kept in memory via IndexedDB even if you save it via FileSystem plugin (who wrap your file into indexexDB).

Your second approach <img [src]="base64Src" /> is the right one. The base64 come from the IndexedDB database created by FileSystem plugin.

So, depend on the platform your app is (mobile, web, electron, etc..) you have to readfile() differently.

Hope this can help you.

Jonathan-J8 avatar Apr 21 '21 17:04 Jonathan-J8

Is there any workaround?

goforu avatar Jul 20 '24 13:07 goforu

@goforu, let's say you have a function getAttachmentSrc(path: string) that takes a path and returns a src (usable as src attribute):

async function getAttachmentSrc(path: string): Promise<string> {
  const { uri } = await Filesystem.getUri({
    directory: Directory.Documents,
    path,
  });
  return Capacitor.convertFileSrc(uri);
}

Workaround would be returning a data url on web:

async function getAttachmentSrc(path: string): Promise<string> {
  if (Capacitor.isNativePlatform()) {
    const { uri } = await Filesystem.getUri({
      directory: Directory.Documents,
      path,
    });
    return Capacitor.convertFileSrc(uri);
  } else {
    // work around for web
    const { data } = await Filesystem.readFile({
      directory: Directory.Documents,
      path,
    });
    return URL.createObjectURL(data as Blob);
  }
}

Hope this helps.

abegehr avatar Feb 27 '25 05:02 abegehr