Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

iOS - Image files included in outbound network requests are significantly larger than the original files #33760

Open
emily-curry opened this issue May 4, 2022 · 10 comments
Labels
Component: Image Never gets stale Prevent those issues and PRs from getting stale Platform: iOS iOS applications. Type: Old Architecture For issues related to the old architecture

Comments

@emily-curry
Copy link

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.

@cipolleschi cipolleschi added Priority: Low Tech: React Native Core Issue related to the Core of React Native Type: Old Architecture For issues related to the old architecture Impact: Bug The issue represents a bug somewhere and removed Needs: Triage 🔍 labels May 18, 2022
@ntdiary
Copy link

ntdiary commented Jul 27, 2022

the issue #31641 is similar to here.
Maybe that issue can be closed.

temporary solution

There are already several solutions in the #27099 (comment) ,
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:
084F468D-5919-4048-918D-94BF4975A9E2

I tested these two scenarios (use form-data to upload compressed jpeg/png image):

  1. There is no Image component in the app.
    RCTNetworking will use RCTFileRequestHandler to handle image file, the file size is same.
    Because _loaders in RCTImageLoader.mm is not initialized.
  2. There is Image component in the app.
    RCTNetworking will use RCTImageLoader sendRequest to 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)

  1. Static Image Resources ( e.g. require('./my-icon.png') )
    the uri is resolved to http://localhost:8081/assets/src/my-icon.png?platform=ios&hash=0d42d44c5312fdb4a2e7d42a9bfa8b0c
    stack: RCTImageLoader --> RCTNetworking --> RCTHTTPRequestHandler.
  2. Xcode asset catalogs ( e.g. uri: 'app_icon')
    the uri is resolved to file:///xxx/xxx.png
    stack: RCTImageLoader --> RCTLocalAssetImageLoader
  3. Network Images, is same with Static Image Resources
  4. Uri Data Images
    this uri is data:data:image/png;base64,xxxx
    stack: 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. 🙂

@chrisbobbe
Copy link

chrisbobbe commented Dec 16, 2022

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 e83feff—I don't see any public discussion of that—and try to land an amended fix.

@cortinico
Copy link
Contributor

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

@GiovanniVisentiniCasavo

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)

@ramble-lo
Copy link

ramble-lo commented Nov 3, 2023

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

@mpoweredo
Copy link

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

Thanks buddy - looks like it is working right now! :)
Passing base64 string through components and functions won't make app laggy?

@react-native-bot
Copy link
Collaborator

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.

@react-native-bot react-native-bot added the Stale There has been a lack of activity on this issue and it may be closed soon. label Aug 11, 2024
@react-native-bot
Copy link
Collaborator

This issue was closed because it has been stalled for 7 days with no activity.

@react-native-bot react-native-bot closed this as not planned Won't fix, can't repro, duplicate, stale Aug 18, 2024
@stianjensen
Copy link
Contributor

This issue is not stale! We're still waiting for the fix that was backed out in e83feffe to be re-landed or replaced with a new fix.

@cortinico cortinico reopened this Sep 17, 2024
@cortinico cortinico added Never gets stale Prevent those issues and PRs from getting stale and removed Impact: Bug The issue represents a bug somewhere Stale There has been a lack of activity on this issue and it may be closed soon. Priority: Low Tech: React Native Core Issue related to the Core of React Native labels Sep 17, 2024
@cortinico
Copy link
Contributor

Reopening as we believe this is still an issue.

Also related to:

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Component: Image Never gets stale Prevent those issues and PRs from getting stale Platform: iOS iOS applications. Type: Old Architecture For issues related to the old architecture
Projects
None yet
Development

No branches or pull requests

11 participants