Open stefan-schweiger opened 1 year ago
:warning: | Newer Version of React Native is Available! |
---|---|
:information_source: | You are on a supported minor version, but it looks like there's a newer patch available - 0.72.6. Please upgrade to the highest patch for your minor or latest and verify if the issue persists (alternatively, create a new project and repro the issue in it). If it does not repro, please let us know so we can close out this issue. This helps us ensure we are looking at issues that still exist in the most recent releases. |
Still happens for patch 0.72.6, I have updated the issue, please remove the tag
After a bit more tinkering I think it's really that the Blob implementation does not understand Uint8Arrays, because if I do this (effectively constructing a normal array) I get the same result on the web as I do on a native device: new Blob([b64.decode(b64Data).split('').map(x => x.charCodeAt(0))], { type: contentType })
@CodyJasonBennett I saw that you implemented some additional Blob code which is not yet released that allows for Blobs to be constructed from ArrayBuffer. From what I was able to gather I think that the changes you did would also allow for Blobs to be constructed from a Uint8Array, but there is no explicit code branch for it here:
Is that assessment correct?
EDIT: I just tried to create a small patch myself which uses 'base64-js'. But this still produces an unusable blob which just contains the base64 content as a string...
if (part instanceof Uint8Array) {
return {
data: fromByteArray(part),
type: 'string'
}
}
@CodyJasonBennett how confident are you in your implementation? I just applied all your changes as a patch and tried to construct a blob from an ArrayBuffer and it just gave me a blob which is a basically just the buffer content as text.
(The output is the value the blob actually should have if it was base64 encoded)
For a second I though that this might at least be a workaround return new Blob([b64.decode(b64Data)], { type: contentType });
but for an PDF this actually still creates a corrupted blob which when viewed in a hex editor has the correct PDF headers and metadata but the actual binary data within seems corrupted. I assume this is because of some wrong interpretation of the string encoding (e.g. UTF8 vs ASCII).
I assume this is because of some wrong interpretation of the string encoding (e.g. UTF8 vs ASCII).
I'm pretty sure this assumption is correct, and consequently, I'm inclined to revert #39276 until we do this properly. My previous code example did not run into this since text was incidentally decoded by FileReader
. We'll need better coverage to okay a PR like that, and I noted that it is still divergent from web from byte size alone since there is no native ArrayBuffer
support thus I had to workaround it with base64 (string for JSI) in the first place.
const data = await new Promise((res, rej) => {
const reader = new FileReader()
reader.onload = () => res(reader.result)
reader.onerror = rej
reader.readAsText(blob)
})
// `data:${blob.type};base64,${data}`
``
@CodyJasonBennett If you decide to revert the changes maybe there should also be an Error throwing if a part is Uint8Array
similar to the "old" check for ArrayBuffer
.
If you decide to revert the changes maybe there should also be an Error throwing if a part is Uint8Array similar to the "old" check for ArrayBuffer.
I'm not sure what you mean. The old and current checks consider all typed arrays -- that's what ArrayBuffer.isView
does.
how confident are you in your implementation? I just applied all your changes as a patch and tried to construct a blob from an ArrayBuffer and it just gave me a blob which is a basically just the buffer content as text.
Upon further review that PR is working as I initially intended even if incomplete -- this is the equivalent code for web btoa(String.fromCharCode(...new Uint8Array()))
which converts from UTF-16 to base64-encoded ASCII. Notably, readAsText
as used in the above example defaults to UTF-8
for encoding which hides this discrepancy since it converts under the hood.
That's obviously not desirable here in this issue though. I'm not keen on handling encoding for arbitrary binary data, but I wonder if we can get away with UTF or some other encoding to bring it closer to web. I can't find anything concrete on what the internal behavior should look like as everything states that ASCII should be used for safe transmission.
I'm not sure what you mean. The old and current checks consider all typed arrays -- that's what
ArrayBuffer.isView
does.
In the "old" implementation this was the check, which did not throw an Errror if a part is Uint8Array
:
if (
part instanceof ArrayBuffer ||
(global.ArrayBufferView && part instanceof global.ArrayBufferView)
) {
throw new Error(
"Creating blobs from 'ArrayBuffer' and 'ArrayBufferView' are not supported",
);
}
This issue is stale because it has been open 180 days with no activity. Remove stale label or comment or this will be closed in 7 days.
not stale
On react native 0.75.1 after enabling new arch + bridgeless this got completely broken. The above example throws:
{"stack":"Error: Creating blobs from 'ArrayBuffer' and 'ArrayBufferView' are not supported\n at anonymous (http://localhost:8081/index.bundle//&platform=ios&dev=true&lazy=true&minify=false&inlineSourceMap=false&modulesOnly=false&runModule=true&app=me.bluemail.comm:59350:28)\n at map (native)\n at createFromParts (http://localhost:8081/index.bundle//&platform=ios&dev=true&lazy=true&minify=false&inlineSourceMap=false&modulesOnly=false&runModule=true&app=me.bluemail.comm:59348:30)\n at Blob (http://localhost:8081/index.bundle//&platform=ios&dev=true&lazy=true&minify=false&inlineSourceMap=false&modulesOnly=false&runModule=true&app=me.bluemail.comm:59610:46)\n at base64ToBlob (JavaScript:9:18)\n at eval (JavaScript:1:29)\n at __tickleJs (__tickleJsHackUrl:1:43)","message":"Creating blobs from 'ArrayBuffer' and 'ArrayBufferView' are not supported"}
I added workaround in form of turning the chunk into base64 before:
function chunkToBase64(chunk) { let binary = ''; let bytes = new Uint8Array(chunk); for (let i = 0; i < bytes.byteLength; i++) { binary += String.fromCharCode(bytes[i]); } return btoa(binary); // Convert to base64 }
tho this could potentially affect performance. Does anyone has a better solution / will this be fixed?
related: #44125
Hello everyone, I'm facing the same problem when working with new Uint8Array, I'm using expo in its latest version (51.0.36) and react native in version 0.74.5, I need to do this conversion because I have an api in .Net which only receives a specific type of file, is there anything to solve this problem?
Description
I'm trying to manually convert base64 data to a Blob and vice versa. To do this I create a
Uint8Array
from the base64 data (withatob
- polyfilled withbase-64
).For example for a very simple PNG the resulting blob is about 3-4x as large as expected and the data is unrecognizable.
The same code works fine on the web and outputs the same result for both log statements.
React Native Version
0.72.6
Output of
npx react-native info
System: OS: macOS 14.0 CPU: (10) arm64 Apple M1 Pro Memory: 551.34 MB / 32.00 GB Shell: version: "5.9" path: /bin/zsh Binaries: Node: version: 20.8.0 path: /opt/homebrew/bin/node Yarn: version: 1.22.19 path: /usr/local/bin/yarn npm: version: 10.2.0 path: /opt/homebrew/bin/npm Watchman: version: 2023.10.09.00 path: /opt/homebrew/bin/watchman Managers: CocoaPods: version: 1.13.0 path: /opt/homebrew/bin/pod SDKs: iOS SDK: Platforms:
Steps to reproduce
base-64
Snack, screenshot, or link to a repository
https://snack.expo.dev/@stefan-5gpay/base64blobbug
On the web version both are the same:
On device (same for iOS and Android) data is different: