react-native
react-native copied to clipboard
iOS - Image files included in outbound network requests are significantly larger than the original files
Description
When creating a multipart/form-data request with a FormData object whose parts contain a uri property pointing to an image file, the resulting network request is much (~3-4 times) larger than the original image file.
This issue only occurs on iOS. This is a duplicate of #27099, which has been closed but is not fixed.
Version
0.67.3
Output of npx react-native info
System:
OS: macOS 12.3.1
CPU: (12) x64 Intel(R) Core(TM) i7-9750H CPU @ 2.60GHz
Memory: 973.39 MB / 32.00 GB
Shell: 3.3.1 - /usr/local/bin/fish
Binaries:
Node: 16.14.2 - /usr/local/bin/node
Yarn: 1.22.17 - /usr/local/bin/yarn
npm: 8.8.0 - /usr/local/bin/npm
Watchman: 2022.03.21.00 - /usr/local/bin/watchman
Managers:
CocoaPods: 1.11.3 - /usr/local/bin/pod
SDKs:
iOS SDK:
Platforms: DriverKit 21.4, iOS 15.4, macOS 12.3, tvOS 15.4, watchOS 8.5
Android SDK:
API Levels: 28, 29, 30, 31
Build Tools: 28.0.3, 29.0.2, 29.0.3, 30.0.1, 30.0.2, 30.0.3, 31.0.0, 32.0.0, 32.1.0
System Images: android-23 | Google APIs Intel x86 Atom_64, android-24 | Google APIs Intel x86 Atom, android-24 | Google Play Intel x86 Atom, android-26 | Google Play Intel x86 Atom, android-28 | Google Play Intel x86 Atom, android-29 | Google Play Intel x86 Atom, android-30 | Google Play Intel x86 Atom, android-31 | Google Play Intel x86 Atom_64
Android NDK: Not Found
IDEs:
Android Studio: 2021.1 AI-211.7628.21.2111.8309675
Xcode: 13.3.1/13E500a - /usr/bin/xcodebuild
Languages:
Java: 11.0.11 - /usr/bin/javac
npmPackages:
@react-native-community/cli: Not Found
react: 17.0.2 => 17.0.2
react-native: 0.67.3 => 0.67.3
react-native-macos: Not Found
npmGlobalPackages:
*react-native*: Not Found
Steps to reproduce
Obtain a URI to any on-disk image, call it fileUri.
Create a FormData object with the file uri:
const formData = new FormData();
formData.append('file', { type: 'application/octet-stream', name: 'image.jpeg', uri: fileUri });
Then, make a request with that form data:
const response = await fetch('https://postman-echo.com/post', { method: 'POST', body: formData })
Observe the content-length of the request headers if you control the server, or in this example, observe the response body from postman-echo.
Snack, code example, screenshot, or link to a repository
Snack demonstrating issue (and current workaround): https://snack.expo.dev/@httpriestess/image-upload-size-repro
It posts an image file to postman-echo, and then displays the content-length header that postman-echo returns.
the issue https://github.com/facebook/react-native/issues/31641 is similar to here. Maybe that issue can be closed.
temporary solution
There are already several solutions in the https://github.com/facebook/react-native/issues/27099#issuecomment-1012775499 ,
such as copying the file and changing the image file extension or using rn-fetch-blob library ( I didn't verify ).
Here are another solution:
customize a new handler to handle the request with file:// sheme and image type
add these two files to our own project. OriginImageRequestHandler.h OriginImageRequestHandler.mm
These two files just copy the contents of RCTFileRequestHandler and increase the priority to 4 ( RCTImageLoader is 2 ).
We can customize canHandleRequest to handle the specified file if needed ( I only upload images in tmp directory ).
Image and Networking
Here are default handlers in RCTNetworking.mm:

I tested these two scenarios (use form-data to upload compressed jpeg/png image):
- There is no Image component in the app.
RCTNetworkingwill useRCTFileRequestHandlerto handle image file, the file size is same. Because_loadersinRCTImageLoader.mmis not initialized. - There is Image component in the app.
RCTNetworkingwill useRCTImageLoadersendRequestto handle image file, the file size is larger.
Of course, if these original images conform to the apple png/jpeg image storage format, the size will not become larger.
( e.g. upload an image saved using UIImagePNGRepresentation or UIImageJPEGRepresentation(image, 1.0) )
In addition. Here are some simplified call stacks for Image component (refrence)
- Static Image Resources ( e.g.
require('./my-icon.png')) the uri is resolved tohttp://localhost:8081/assets/src/my-icon.png?platform=ios&hash=0d42d44c5312fdb4a2e7d42a9bfa8b0cstack:RCTImageLoader-->RCTNetworking-->RCTHTTPRequestHandler. - Xcode asset catalogs ( e.g.
uri: 'app_icon') the uri is resolved tofile:///xxx/xxx.pngstack:RCTImageLoader-->RCTLocalAssetImageLoader - Network Images, is same with Static Image Resources
- Uri Data Images
this uri is
data:data:image/png;base64,xxxxstack:RCTImageLoader-->RCTNetworking-->RCTDataRequestHandler.
related commits in history
f88bc3eb
The currently implemented handlers are: ...
- RCTImageRequestHandler - a handler for loading local images from the iOS asset-library
this revision added file RCTImageRequestHandler.m to load local image with assets-library or ph sheme
36444a65
Add pluggable image processing system
this revision deleted RCTImageRequestHandler.m file and moved its function to the RCTImageLoader.m,
added RCTAssetBundleImageLoader.m to load local image (Its current name is RCTLocalAssetImageLoader.mm)
f78526ce
Avoid re-encoding images when uploading local files (#31457)
this revision added a solution
e83feffe
Back out "Avoid re-encoding images when uploading local files" Summary: This was causing an upload error in FB Dating, will need to re-land with the fix.
this revision reverted f78526ce
Finally
If there is something wrong with the above, feel free to tell me. 🙂
Is there anything in the new architecture that we'd expect to fix this issue? I see this issue was marked with "Type: Old Architecture".
I think a good next step is to find out why the fix in #31457 was reverted, in e83feffe—I don't see any public discussion of that—and try to land an amended fix.
Is there anything in the new architecture that we'd expect to fix this issue? I see this issue was marked with "Type: Old Architecture".
Not really as this is related to networking/images which is unrelated to the New Architecture at all
this story is amazing.. A fix reverted for a dating app..
@ntdiary thanks for you solution, but I implemented it in a different way:
in my opinion we can just define the handlerPriority in RCTFileRequestHandler, in this way you don't need to copy the file -> duplicate the code..
this handlerPriority looks to be used only in the network directory so I hope it is safe to do it..
By the way the images are display in the app, and the upload is correctly done..
here the patch:
diff --git a/node_modules/react-native/Libraries/Network/RCTFileRequestHandler.mm b/node_modules/react-native/Libraries/Network/RCTFileRequestHandler.mm
index 19d025c..519c860 100644
--- a/node_modules/react-native/Libraries/Network/RCTFileRequestHandler.mm
+++ b/node_modules/react-native/Libraries/Network/RCTFileRequestHandler.mm
@@ -92,6 +92,22 @@ - (void)cancelRequest:(NSOperation *)op
return nullptr;
}
+/**
+ * Add this function to give precedence to the file upload instead of image upload. the image upload is changing the file
+ * with no reason.
+ * this handlerPriority is used only in the RCTNetworking.mm and this file is in defined in the network directory so I hope is not used
+ * else where
+ * for the bug see:
+ * https://github.com/facebook/react-native/issues/27099
+ * https://github.com/facebook/react-native/pull/31457
+ * https://github.com/facebook/react-native/commit/e83feffeba567b4e304181632128ee1caa036c45
+ * https://github.com/facebook/react-native/issues/33760
+*/
+- (float)handlerPriority
+{
+ return 3;
+}
+
@end
Class RCTFileRequestHandlerCls(void)
I don't know why it was work, but I think maybe this can help guys.
I have the same issue, but when I try to add the base64 into formData the issue was been solved.
from
formData.append('file', { type: 'image/jpg', name: 'image.jpg', uri: fileUri });
to
formData.append('file', { type: 'image/jpg', name: 'image.jpg', uri: fileUri, base64: '/9j/4AA.....' });
*I get these data by react-native-image-picker
I don't know why it was work, but I think maybe this can help guys.
I have the same issue, but when I try to add the base64 into
formDatathe issue was been solved.from
formData.append('file', { type: 'image/jpg', name: 'image.jpg', uri: fileUri });to
formData.append('file', { type: 'image/jpg', name: 'image.jpg', uri: fileUri, base64: '/9j/4AA.....' });*I get these data by react-native-image-picker
Thanks buddy - looks like it is working right now! :) Passing base64 string through components and functions won't make app laggy?
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.
This issue was closed because it has been stalled for 7 days with no activity.
This issue is not stale! We're still waiting for the fix that was backed out in https://github.com/facebook/react-native/commit/e83feffe to be re-landed or replaced with a new fix.
Reopening as we believe this is still an issue.
Also related to:
- https://github.com/facebook/react-native/issues/27099