capacitor icon indicating copy to clipboard operation
capacitor copied to clipboard

Support blob/large data

Open mlynch opened this issue 6 years ago • 21 comments

Investigate passing blobs from native to JS and vice versa.

mlynch avatar Nov 20 '18 05:11 mlynch

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

jcesarmobile avatar Nov 20 '18 08:11 jcesarmobile

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

thmclellan avatar Aug 19 '19 19:08 thmclellan

Fetch should work, but you have to use convertFileSrc on the file:// url

jcesarmobile avatar Aug 19 '19 20:08 jcesarmobile

@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.

thmclellan avatar Aug 19 '19 21:08 thmclellan

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()

black-hawk85 avatar Nov 16 '19 22:11 black-hawk85

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?

digaus avatar Mar 10 '20 15:03 digaus

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?

diachedelic avatar Apr 15 '20 00:04 diachedelic

I've implemented this functionality as a plugin, see https://github.com/diachedelic/capacitor-blob-writer

diachedelic avatar May 08 '20 13:05 diachedelic

Blob writer plugin looks like an elegant solution, impressive benchmarks. Thanks @diachedelic for sharing!

thmclellan avatar May 08 '20 18:05 thmclellan

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!

jfbloom22 avatar May 25 '21 20:05 jfbloom22

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

spartanatreyu avatar Jun 01 '21 03:06 spartanatreyu

Looking forward to this too. Meanwhile I am using:

and

peterpeterparker avatar Jun 13 '21 19:06 peterpeterparker

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?

idcipher106 avatar Mar 01 '22 18:03 idcipher106

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.

dylanvdmerwe avatar Mar 28 '22 19:03 dylanvdmerwe

@mlynch @jcesarmobile any chance this could be added to the 4.0 Milestone? 😍

mesqueeb avatar May 19 '22 05:05 mesqueeb

Any update?

iamcco avatar Jun 08 '22 16:06 iamcco

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:

  1. The ability to download a large file straight into the device filesystem (FileTransferPlugin as this fails in "classic" cordova)
  2. 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 avatar Jul 29 '22 18:07 HarelM

@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.

spartanatreyu avatar Aug 01 '22 00:08 spartanatreyu

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 avatar Aug 01 '22 06:08 HarelM

@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 avatar Aug 01 '22 23:08 spartanatreyu

@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... :-)

HarelM avatar Aug 02 '22 09:08 HarelM

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,
            });
        }

malte94 avatar Sep 25 '22 11:09 malte94

@mlynch @jcesarmobile any chance this could be added to the 5.0 Milestone? 😍

mesqueeb avatar Oct 18 '22 04:10 mesqueeb

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

qrclip avatar Mar 24 '23 12:03 qrclip

Thanks @qrclip! Does this plugin supports downloading large files as well? I'm looking into removing cordova file and cordova file transfer plugins.

HarelM avatar Mar 25 '23 08:03 HarelM

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: @.***>

qrclip avatar Mar 25 '23 10:03 qrclip

I've opened the first issue in your repo and provided my feedback, thanks! :-) https://github.com/qrclip/capacitor-file-chunk/issues/1

HarelM avatar Mar 26 '23 08:03 HarelM

Great, I will do my best ... after all, it's the first issue! 🥇

qrclip avatar Mar 26 '23 08:03 qrclip

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.

markemer avatar Jan 18 '24 16:01 markemer

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?

HarelM avatar Jan 18 '24 16:01 HarelM