apache / cordova-plugin-file

Apache Cordova File Plugin
https://cordova.apache.org/
Apache License 2.0
742 stars 762 forks source link

Application crash with big data (JSON) #311

Open grizio opened 5 years ago

grizio commented 5 years ago

Bug Report

Problem

What is expected to happen?

The (big) data is correctly written in the file.

What does actually happen?

The application crashes.

💡

I found a workaround but I think it remains an issue in the codebase (and could impact other developers). See below for the workaround.

Information

I use:

For the persistance (with redux-persist), I have a custom storage handler to write into a file. The write part is very simple :

function writeFile(fileEntry: FileEntry, data: any): Promise<void> {
  return new Promise((resolve, reject) => {
    fileEntry.createWriter(writer => {
      writer.onerror = reject
      writer.onwriteend = resolve
      writer.write(new Blob([JSON.stringify(data)], { type: 'text/plain' }))
    }, reject)
  })
}

However, with an evolution, our data passed from ~2Mo to ~20Mo. At this moment, the application crashes when saving the file. It is the call to writer.write which seemed to cause the application to crash, if I build the Blob without saving it, I do not have any issue.

The size of the data before crash differs from IOS and ipad version.

Workaround

I corrected (quick-win) the issue by doing so:

function writeFile(fileEntry: FileEntry, data: any): Promise<void> {
  return new Promise((resolve, reject) => {
    fileEntry.createWriter(writer => {
      splitByChunks(JSON.stringify(data), 1000 * 1000) // 1Mo
        // Ensure order of execution, could be improved
        .reduce((acc, chunk) => acc.then(() => writeChunk(writer, chunk)), Promise.resolve())
        .then(resolve)
        .catch(reject)
    }, reject)
  })
}

function writeChunk(writer: FileWriter, chunk: string): Promise<void> {
  return new Promise((resolve, reject) => {
    writer.onerror = reject
    writer.onwriteend = resolve
    writer.write(new Blob([chunk], { type: 'text/plain' }))
  })
}

function splitByChunks(value: string, chunkLength: number): ReadonlyArray<string> {
  return Array(Math.ceil(value.length / chunkLength))
    .fill(0)
    .map((_, index) => value.slice(index * chunkLength, (index + 1) * chunkLength))
}

Although this correction works in my project there is an issue when the file is not totally written (application closed during write). I resolved it by ignoring incomplete file (= JSON.parse fails) because my project permits it.

Environment, Platform, Device

Version information

Checklist

Thanks for the plugin! 👍

be-ndee commented 5 years ago

I have the same problem. Trying to write big files (like 16MB) makes the app crash with a white-screen. Using chunks seems to solve the problem.

Polm90 commented 5 years ago

I have the same issue with pdf and mp4 files too. Tested on a device with iOS 12.4 Cordova 9.0.0, cordova-plugin-file 6.0.2, cordova-ios 5.0.1. No issue on cordova-browser 6.0.0

LightMind commented 3 years ago

See my comment here, for an explanation why FileWriter.write can crash your app: https://github.com/apache/cordova-plugin-file/issues/364#issuecomment-773977157