capacitor
capacitor copied to clipboard
Support blob/large data
Investigate passing blobs from native to JS and vice versa.
There is this issue about improvements and one of the is about blobs. Should we close this one or better to have an issue per improvement? https://github.com/ionic-team/capacitor/issues/974
Great to see this on the roadmap. In the meantime, if working on a custom Capacitor plugin, are there any creative workarounds that might allow returning a blob/binary data without having to convert to JSON? Based on https://capacitor.ionicframework.com/docs/plugins/ios, it seems like the CAPPluginCall can only handle JSON right now, and that no amount of creative plugin writing could respond with a blob directly. Is that right?
The background here is that we're trying to let the user import and upload 20MB PDF files with our app. The current workaround is converting the binary data to base64 using FileSystem.readFile, and then converting it back to a blob in the app, and uploading to a remote server as XHR. Trying to avoid memory issues, and unable to do a direct Fetch on a non-HTTP URI (e.g. file://etc on iOS). Thanks
Fetch should work, but you have to use convertFileSrc on the file:// url
@jcesarmobile thank you! I was using convertFileSrc while running ng serve and getting a CORS error on the "capacitor://localhost" URL, but I see now that fetch is working with convertFileSrc on a production build. For ng serve, after doing convertFileSrc, replacing "capacitor://localhost" with window.location.origin gave me a URL that works just fine for fetch. Appreciate the quick help.
Sharing my workaround with you.
I'm using web-implementation of the Filesystem plugin. Don't know if it's a good solution but it helps solving my problem.
import { FilesystemDirectory, FilesystemEncoding } from '@capacitor/core'
// const { Filesystem } = Plugins // => Doesn't work with blob in native
// Workaround: Use Web-Implementation of plugin
import { FilesystemPluginWeb } from '@capacitor/core/dist/esm/web/filesystem.js'
const Filesystem = new FilesystemPluginWeb()
I was able to create a service for writing and reading large blobs. (For web, blobs are already supported for indexeddb)
Reading is simply done with a fetch
call.
Writing is a bit more tricky because we can only pass strings to that native layer.
Converting a large blob to a base64 string will crash the webview because the size is simply to big to handle. However we can split the blob into chunks and convert these smaller chunks to a base64 string. Then we can just use appendFile
to stitch it back together.
@jcesarmobile
Would it be possible to integrate this into the Filesystem
plugin so it is supported directly? Would you consider a the PR if I make one? Or is there a better solution?
I just had an idea of how we could theoretically stream large amounts of binary data directly to the filesystem from the webview. We could avoid the strings-only bridge by making a POST
request which is intercepted by Capacitor, and the body written to disk. This could either be implemented within Filesystem.writeFile
and Filesystem.appendFile
, and/or be exposed as an HTTP endpoint like so:
const res = await fetch('https://stuff.com/video.mp4')
const myVideoBlob = await res.body()
// write options like `recursive` could be passed in this query string?
const writeUrl = '/_capacitor_file_/data/user/0/com.example.app/files/videos/1.mp4?recursive=true'
await fetch(writeUrl, {
method: 'POST',
body: myVideoBlob,
})
Thoughts?
I've implemented this functionality as a plugin, see https://github.com/diachedelic/capacitor-blob-writer
Blob writer plugin looks like an elegant solution, impressive benchmarks. Thanks @diachedelic for sharing!
I am upgrading to Capacitor 3 today and I was really hoping this made it in. Sad that it is not in there yet. Hope it makes it in there soon!
Some meta commentary: this is now the 6th (and related to the 4th) highest rated issue on Capacitor and blocking the oldest issue in Capacitor's plugin repo
Looking forward to this too. Meanwhile I am using:
- the cordova-plugin-x-socialsharing and file plugins for native apps
and
- the web share api on the web (when available and when file extensions are permitted)
Seems to me this is a gigantic hole in the capacitor ecosystem. I mean, binary data is a thing; and not everything is a "string"... The idea that capacitor has no real support for anything but strings seems pretty short-sighted. On our project we have a need to pass an ArrayBuffer from JavaScript to a capacitor plugin that we are writing. I thought for sure this would "obviously be possible", but alas here I am and I'm amazed that the only solution is kludgy workarounds. In my case I'll convert to base64 because the size of the binary data is always smaller than around 50kB. But come on man... how can this still not be fixed?
We have also managed to hit this problem. Filesystem should work with blobs - we cannot have files being saved and read as base64. Blobs are being used by the native code and then converted to base64 anyways.
@mlynch @jcesarmobile any chance this could be added to the 4.0 Milestone? 😍
Any update?
I'm in the middle of migrating my app from cordova to capacitor. For offline usages I download a large file (100Mb) and then unzip it (in memory in JS code) and store its data in indexDB. This unfortunately crashes when using the capacitor filesystem plugin. I'm currently using two plugins which I wish I could remove:
- The ability to download a large file straight into the device filesystem (FileTransferPlugin as this fails in "classic" cordova)
- The ability to open a large file and use it as a blob (or arraybuffer)
Here's my current code and future code if anyone is interested to see how this is being used in my app: Currently using cordova file plugin and file transfer plugin https://github.com/IsraelHikingMap/Site/blob/e10acfbfcc15d7f4601bb923581254f3d1ee1f68/IsraelHiking.Web/src/application/services/file.service.ts#L346L354 https://github.com/IsraelHikingMap/Site/blob/7784673508d3ee8ce22e91e04b74af818295d7f5/IsraelHiking.Web/src/application/services/file.service.ts#L339L350
@HarelM I'm not sure about the unzipping part, but I was able to get large files (~100mb) to save to the file system by downloading the file in multiple chunks using the Range
http header. My code saved the first chunk to the file system, then appending the following chunks to the end until the file was complete.
That's very nice! But I would love this to be a part of the file system plugin as I don't think every developer using capacitor should implement this. If not implemented, I would suggest to add your example in the docs so that people can use it in these "edge" cases. In my case, since I can't read large files and I need to download large files I use cordova-plugin-file and cordiva-plugin-file-transfer which work well.
@HarelM I've went back through an app I've made and grabbed some chunks that touch the capacitor fs plugin and removed anything that isn't capacitor related.
Some parts are a little messy.
Feel free to peruse it, hopefully it can help more people than the capacitor docs did.
https://gist.github.com/spartanatreyu/6ba9dd416b9a9a5b3ccd6026ecfd1de1
@spartanatreyu thanks for sharing!! What you wrote is amazing :-) Having said that, I would prefer if some of it will happen in the native code in order to avoid blocking the UI thread in some cases and in general, while I can achieve using what you wrote there, I prefer to have it as part of the capacitor library. The current solution of taking the file plugin and file transfer plugin is good enough so that I don't need to find alternatives. This will not be the case if this was part of this plugin of course as I would prefer to use capacitor plugins and reduce the dependency on Cordova plugins (due to maintenance issues). I have a similar abstraction layer here: https://github.com/IsraelHikingMap/Site/blob/07cbd143b3839ff02b3545bbc768dd324a7b541a/IsraelHiking.Web/src/application/services/file.service.ts Which in theory can be replaced by your code in some or most places, but again, I'm not sure there's a good benefit for replacing the code... If the capacitor team would like to point me in the right direction in terms of where and maybe how to write this code I would be happy to donate my time as I think this is an important project and I will relay heavily on it once the migration is complete. but I'll need some guidance and help to achieve this... :-)
I encounter problems with UTF-8 encoding. Umlauts in German or currencies ($, €) are converted wrongly. I do not encounter this issue when using the Browser API to generate a download link.
if (content instanceof Blob) {
write_blob({
data: content,
path: filename,
directory: Directory.Cache
});
} else {
await Filesystem.writeFile({
path: filename,
data: content,
directory: Directory.Cache,
});
}
@mlynch @jcesarmobile any chance this could be added to the 5.0 Milestone? 😍
To address this issue, I created a plugin specifically designed to handle large files. It is based on the capacitor-blob-writer, but with added capabilities for reading and writing files in chunks, as well as encryption support for improved security. I encourage you to try out this plugin : https://www.npmjs.com/package/capacitor-file-chunk
Thanks @qrclip! Does this plugin supports downloading large files as well? I'm looking into removing cordova file and cordova file transfer plugins.
Hello, This plugin does not directly support downloading large files, but you can use, for example, the range request headers to download files in chunks and merge them. While the Capacitor File Chunk plugin is not a direct replacement for Cordova file and file transfer plugins, it can still be helpful when dealing with large files.For my use case, I already saved the files in 50MB chunks, so all I had to do was merge them. I could do that with the filesystem, but it was slow. Then I had to read, which was possible with the Dom file, but it was not possible with a file from the filesystem that I shared with my application. With this plugin I can. I plan to add more examples to the documentation and would be happy to start with a use case that fits your needs. Please let me know more about the large file(s) you would like to download and any special requirements or limitations you have in mind. Once I have a better understanding of your scenario, I can create a custom example that shows how you can use the Capacitor File Chunk plugin to download large files in chunks. Your feedback and suggestions are valuable to improve the documentation of the plugin for other users as well.
A sábado, 25/03/2023, 08:26, Harel M @.***> escreveu:
Thanks @qrclip https://github.com/qrclip! Does this plugin supports downloading large files as well? I'm looking into removing cordova file and cordova file transfer plugins.
— Reply to this email directly, view it on GitHub https://github.com/ionic-team/capacitor/issues/984#issuecomment-1483762848, or unsubscribe https://github.com/notifications/unsubscribe-auth/AULCA7SDKAGLNQ457QAQI2LW52TZRANCNFSM4GFJRHTQ . You are receiving this because you were mentioned.Message ID: @.***>
I've opened the first issue in your repo and provided my feedback, thanks! :-) https://github.com/qrclip/capacitor-file-chunk/issues/1
Great, I will do my best ... after all, it's the first issue! 🥇
Stale, and this should be handled by plugins - re-file under @ionic-team/capacitor-plugins if it's still an issue and an issue isn't open there.
This is still very much an issue. I'm currently using the Cordova file plugin to overcome this issue. Is this planned as part of the file plugin of capacitor?