capacitor: bug: CapacitorHttp - Requesting a Blob fails on native iOS and Android

Bug Report

Capacitor Version

💊   Capacitor Doctor  💊 

Latest Dependencies:

  @capacitor/cli: 4.5.0
  @capacitor/core: 4.5.0
  @capacitor/android: 4.5.0
  @capacitor/ios: 4.5.0

Installed Dependencies:

  @capacitor/cli: 4.5.0
  @capacitor/android: 4.5.0
  @capacitor/core: 4.5.0
  @capacitor/ios: 4.5.0

[success] iOS looking great! 👌
[success] Android looking great! 👌

Platform(s)

Native iOS Native Android

Current Behavior

Hello there! 👋

Using the Angular HttpClient, which uses CapacitorHttp at a lower level, making a request to retrieve a Blob on native iOS and Android fails. Native iOS without CapacitorHttp, Native Android without CapacitorHttp, and Web work fine.

In our production app, we use a POST request to retrieve a Blob, but using a GET seems to fail as well and was easier to use in a repro.

Expected Behavior

Native iOS and native Android should successfully retrieve the requested Blob.

Also, if we want CapacitorHttp to work the same as XHR/fetch, this would be a difference.

Code Reproduction

https://github.com/KevinKelchen/capacitor-http-request-blob-issue#steps-to-reproduce

Other Technical Details

npm --version output: 8.15.0

node --version output: v16.17.0

pod --version output (iOS issues only): 1.11.3

Additional Context

Thanks so much! 😀

Kevin

About this issue

  • Original URL
  • State: closed
  • Created 2 years ago
  • Reactions: 14
  • Comments: 22

Most upvoted comments

No fix for this yet ?

Requesting blob/arraybuffer via http client should return blob/arraybuffer, not some kind of base64 string. If it’s a technical problem, a conversion to proper type should be done on capacitor side. It’s misleading and makes using external components like ngx-extended-pdf-viewer or ng-lazy-load-image surprisingly broken. I already lost a few hours when debugging why PDFs and images does not load on mobile.

@KevinKelchen : on further investigation, it’s less about blob() and more about requesting anything that isn’t a text/utf8 type file.

So if I fetch an JSON endpoint and call blob, it works. But if I fetch an image, to save it locally, and call blob (or text() or any other method) it fails.

idk how people can work with this for months…

any updates?

Found this issue where the original author explains this behavior: https://github.com/capacitor-community/http/issues/176 Given the technical difficulty I think it’s acceptable that it keeps returning b64. It’s not like I’m doing some heavy computing and this extra encoding/decoding creates a huge problem for me. Someone else might be doing that though. However this definitely should be documented. Currently the doc has no mentioning on usage of b64, and .data happens to be any. We ask for blob, there is a Blob type, of course we expect it to be Blob. Nonetheless it returns string. And now we are left to guess what the string is.

Thanks @kwolfy - you’re absolutely right!

I can confirm success in downloading PDFs as arraybuffers if the Base64 encoding is removed.

The native Android HttpRequestHandler.java is encoding arraybuffers and blobs as Base64 here: https://github.com/ionic-team/capacitor/blob/51a548b97129998de8c403b2a8eb0421135af812/android/capacitor/src/main/java/com/getcapacitor/plugin/util/HttpRequestHandler.java#L244-L248

And for iOS HttpRequestHandler.swift here: https://github.com/ionic-team/capacitor/blob/51a548b97129998de8c403b2a8eb0421135af812/ios/Capacitor/Capacitor/Plugins/HttpRequestHandler.swift#L134-L137

If I remove that Base64 encoding and just use UTF-8 string encoding to pass the arraybuffer/blob from the native handler to the JS CapacitorHttp plugin, I receive the arraybuffer fine. e.g. for Android in HttpRequestHandler.java replacing: return Base64.encodeToString(result, 0, result.length, Base64.DEFAULT); with just UTF-8 encoding of the byte array: return new String(result, StandardCharsets.UTF_8);

and on the CapacitorHTTP plugin side removing the Base64 handling in core-plugin.ts i.e. for arraybuffer and blob cases replace: data = await readBlobAsBase64(blob); with just: data = blob;

@ItsChaceD - what is your take on this? Would a PR for such a change be accepted?

I can’t see this breaking anything, as I can’t see that arraybuffer or blob requests would ever work (unless a client assumed they would receive Base64 encoded blobs/arraybuffers, but that would be a very odd assumption for mind!) Importantly, this would align the arraybuffer/blob result using the CapacitorHttp plugin with the result without the plugin enabled, which is the goal for the plugin.

The problem is here https://github.com/ionic-team/capacitor/blob/4b039f93d4c0b6b0665429cd573c073286734fc0/core/src/core-plugins.ts#L372-L376

@ItsChaceDI see that it was you who added that line. Can you remember please why you convert blob and arraybuffer to base64 ?

As a workaround, one of my colleagues found this neat trick for images :

CapacitorHttp.get({
    url,
    headers,
    responseType: 'blob', // as any if needed
})
.then((response) =>  'data:image/png;base64,' + response.data))

Didn’t tested for other MIME types, but this one seems to work for images. Use the return value as a img[src] value, should display the image correctly.

As a workaround, one of my colleagues found this neat trick for images :

CapacitorHttp.get({
    url,
    headers,
    responseType: 'blob', // as any if needed
})
.then((response) =>  'data:image/png;base64,' + response.data))

Didn’t tested for other MIME types, but this one seems to work for images. Use the return value as a img[src] value, should display the image correctly.

Just something to watch out for with this solution. It actually may point to what is happening internally causing the error this issue is about. You are getting back a base64 encoded string of the image, when it should be a blob. If the Capacitor team ever fix this issue your implementation of the get method above will break.