Skip to content

Application crash with big data (JSON) #311

@grizio

Description

@grizio

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:

  • cordova-plugin-file
  • react-native
  • redux-persist

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

  • ipad4 / ios10
  • ipad5 / ios12
  • ipad6 / ios12

Version information

  • cordova: 8.1.2
  • "cordova-browser": "5.0.1", // also tested on v6.0.1 - same issue
  • "cordova-ios": "4.5.4",
  • "cordova-plugin-file": "5.0.0",

Checklist

Thanks for the plugin! 👍

Metadata

Metadata

Assignees

No one assigned

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions