ionic-team / ionic-team/capacitor-filesystem
[Filesystem]: inconsistent file read/write action between android and web
- Dominant language
- TypeScript
- Stars
- 6
- Forks
- 15
- Avg merge
- 14m
- Merged PRs (30d)
- 1
Description
## 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`:
- `android` is working great:
- `readFile`:
+ when `encoding = undefined` it will forcibly read the file with `base64` https://github.com/ionic-team/capacitor-plugins/blob/main/filesystem/android/src/main/java/com/capacitorjs/plugins/filesystem/Filesystem.java#L29
+ when `encoding = Encoding` it will be read as normal string https://github.com/ionic-team/capacitor-plugins/blob/main/filesystem/android/src/main/java/com/capacitorjs/plugins/filesystem/Filesystem.java#L27
- `writeFIle`:
+ when `encoding = undefined` input `data` data is decoded and saved as `bin` https://github.com/ionic-team/capacitor-plugins/blob/main/filesystem/android/src/main/java/com/capacitorjs/plugins/filesystem/Filesystem.java#L46
+ when `encoding = Encoding` Input data is accepted as a string `text/plain` https://github.com/ionic-team/capacitor-plugins/blob/main/filesystem/android/src/main/java/com/capacitorjs/plugins/filesystem/Filesystem.java#L38
- `android` has very obvious behaviors like `web` does not:
- `readFile`:
+ when `encoding = undefined` it is read and returns what is in `IndexedDB` https://github.com/ionic-team/capacitor-plugins/blob/main/filesystem/src/web.ts#L165
+ when `encoding = Encoding` it is read and returns what is in `IndexedDB` https://github.com/ionic-team/capacitor-plugins/blob/main/filesystem/src/web.ts#L165
- `writeFile`:
+ when `encoding = undefined` it is check is `base64` and save raw to `IndexedDB` https://github.com/ionic-team/capacitor-plugins/blob/main/filesystem/src/web.ts#L200
+ when `encoding = Encoding` it is check is `base64` and save raw to `IndexedDB` https://github.com/ionic-team/capacitor-plugins/blob/main/filesystem/src/web.ts#L200
the problem is that these actions lead to an unclear status on the `web`
I run on `web`:
```ts
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:
```ts
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:
```ts
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)
}
}
```
Contributor guide
Assessment
This issue has not been assessed yet.