cordova-plugin-file
cordova-plugin-file copied to clipboard
Application crash with big data (JSON)
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
- [x] I searched for existing GitHub issues
- Maybe related: #291, #297
- [x] I updated all Cordova tooling to most recent version
- But rolled back because of some incompatibilities in other parts of the application, issue still happening in last versions
- [x] I included all the necessary information above
Thanks for the plugin! 👍
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.
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
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