Skip to content

[Filesystem]: inconsistent file read/write action between android and web #35

@tachibana-shin

Description

@tachibana-shin

Bug Report

Plugin(s)

@capacitor/filesystem: 4.1.4

Capacitor Version

4x

Platform(s)

  • android
  • web

Bug

Looking through the source code I discovered inconsistent behaviors between android and web:

the problem is that these actions lead to an unclear status on the web
I run on web:

await Filesystem.writeFile({
  path: "test.txt",
  directory: Directory.External,
  data: "ASuZAADVfA==",
  encoding: Encoding.UTF8
})

await Filesystem.writeFile({
  path: "test.bin",
  directory: Directory.External,
  data: "ASuZAADVfA=="
})

sha256('test.txt') === sha256('test.bin') while they are obviously two completely different files text.txt contains a base64 string and text.bin contains a data represented by base64

this behavior becomes even more weird in readFile when I run this code:

await Filesystem.writeFile({
  path: "test.txt",
  directory: Directory.External,
  data: "hello world",
  encoding: Encoding.UTF8
})

const base64 = await Filesystem.readFile({
  path: "test.txt",
  directory: Directory.External,
}).then(res => res.data)

console.log(base64) // "hello world" 

this result doesn't make sense because apparently base64.encode('hello world') is aGVsbG8gd29ybGQ=

Solution

(here is my workaround)

Encode all to base64 before saving to IndexedDB

This prevents a binary file with base64 of x from looking exactly like a text file containing utf8 of x

Make base64 explicit by saving files with encoding = undefined as ArrayBuffer or Uint8Array

Make base64 explicit by saving files with encoding = undefined as ArrayBuffer or Uint8Array into IndexedDB (this is allowed and a standard of IndexedDB)
then we will have an implementation that looks like this:

function writeFile(options: WriteFileOptions) {
  if (options.encoding) {
    // normal save
  } else {
    const data = base64ToUint8(options.data)
    // normal save
  }
}
function readFile(options: ReadFileOptions) {
  if (options.encoding) {
    // normal read
  } else {
    const data = ... // normal read

   return uint8ToBase64(data)
  }
}

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions