react-native icon indicating copy to clipboard operation
react-native copied to clipboard

Network Error when send file with formData(axios or fetch) on Android. Works fine on IOS.

Open felipeavila-vp opened this issue 1 year ago • 45 comments

Description

When I try to send some post request sending a file with formData I receive a Network Error. Most of the time the request works fine, but sometimes I receive this error. The problem occurs when use Android, works fine with IOS.

Recently I updated my application to react-native 0.74.1(latest), before the version was 0.64.4. In older version all works fine.

Current versions:

  • React native: 0.74.1
  • Node: >= 18
  • React: 18.2.0
image

Steps to reproduce

Only try to submit some files using formData

React Native Version

0.74.1

Affected Platforms

Runtime - Android

Output of npx react-native info

System:
  OS: macOS 14.4
  CPU: (8) x64 Intel(R) Core(TM) i5-8257U CPU @ 1.40GHz
  Memory: 29.93 MB / 8.00 GB
  Shell:
    version: "5.9"
    path: /bin/zsh
Binaries:
  Node:
    version: 18.19.0
    path: /usr/local/bin/node
  Yarn:
    version: 1.22.22
    path: /usr/local/bin/yarn
  npm:
    version: 8.19.4
    path: /usr/local/bin/npm
  Watchman:
    version: 2024.05.06.00
    path: /usr/local/bin/watchman
Managers:
  CocoaPods:
    version: 1.15.2
    path: /usr/local/bin/pod
SDKs:
  iOS SDK:
    Platforms:
      - DriverKit 23.4
      - iOS 17.4
      - macOS 14.4
      - tvOS 17.4
      - visionOS 1.1
      - watchOS 10.4
  Android SDK:
    API Levels:
      - "29"
      - "30"
      - "31"
      - "32"
      - "33"
      - "34"
    Build Tools:
      - 29.0.2
      - 30.0.2
      - 30.0.3
      - 31.0.0
      - 33.0.0
      - 33.0.1
      - 34.0.0
    System Images:
      - android-29 | Google APIs Intel x86 Atom
      - android-29 | Google Play Intel x86 Atom
      - android-30 | Intel x86_64 Atom
      - android-34 | Google APIs Intel x86_64 Atom
      - android-34 | Google Play Intel x86_64 Atom
      - android-VanillaIceCream | Google APIs Intel x86_64 Atom
    Android NDK: Not Found
IDEs:
  Android Studio: 2023.2 AI-232.10300.40.2321.11567975
  Xcode:
    version: 15.3/15E204a
    path: /usr/bin/xcodebuild
Languages:
  Java:
    version: 17.0.10
    path: /usr/bin/javac
  Ruby:
    version: 2.6.10
    path: /usr/bin/ruby
npmPackages:
  "@react-native-community/cli": Not Found
  react:
    installed: 18.2.0
    wanted: 18.2.0
  react-native:
    installed: 0.74.1
    wanted: ^0.74.1
  react-native-macos: Not Found
npmGlobalPackages:
  "*react-native*": Not Found
Android:
  hermesEnabled: true
  newArchEnabled: false
iOS:
  hermesEnabled: true
  newArchEnabled: false

Stacktrace or Logs

No have logs to show

Reproducer

https://github.com

Screenshots and Videos

No response

felipeavila-vp avatar May 23 '24 14:05 felipeavila-vp

:warning: Missing Reproducible Example
:information_source: We could not detect a reproducible example in your issue report. Please provide either:
  • If your bug is UI related: a Snack
  • If your bug is build/update related: use our Reproducer Template. A reproducer needs to be in a GitHub repository under your username.

github-actions[bot] avatar May 23 '24 14:05 github-actions[bot]

This issue still in

const formData = new FormData() formData.append('audio_file', { name: fileName, type: 'audio/mp3', uri: filePath })

    await fetch('http endpoint', {
        body: formData,
        method: 'POST',
        headers: {
            'Content-Type': 'multipart/form-data',
            Authorization:
                'Bearer token'
        }
    })
        .then(res => {
            console.log('test result ', res)
            console.log('test result status ', res.status)
        })
        .catch(e => {
            console.log('test e ', e)
        })

This returns network error

REACT-NATIVE-INFO output:-

System: OS: macOS 14.2.1 CPU: (8) arm64 Apple M1 Memory: 599.88 MB / 16.00 GB Shell: version: "5.9" path: /bin/zsh Binaries: Node: version: 18.19.0 path: ~/.nvm/versions/node/v18.19.0/bin/node Yarn: version: 3.6.4 path: /opt/homebrew/bin/yarn npm: version: 10.2.3 path: ~/.nvm/versions/node/v18.19.0/bin/npm Watchman: version: 2024.04.15.00 path: /opt/homebrew/bin/watchman Managers: CocoaPods: version: 1.15.2 path: /opt/homebrew/bin/pod SDKs: iOS SDK: Platforms: - DriverKit 23.0 - iOS 17.0 - macOS 14.0 - tvOS 17.0 - watchOS 10.0 Android SDK: API Levels: - "27" - "28" - "29" - "30" - "31" - "32" - "33" - "34" Build Tools: - 27.0.3 - 28.0.3 - 29.0.2 - 29.0.3 - 30.0.2 - 30.0.3 - 31.0.0 - 32.0.0 - 32.1.0 - 33.0.0 - 33.0.0 - 33.0.1 - 34.0.0 System Images: - android-32 | Google APIs ARM 64 v8a - android-33 | Google APIs ARM 64 v8a Android NDK: Not Found IDEs: Android Studio: 2023.1 AI-231.9392.1.2311.11330709 Xcode: version: 15.0/15A240d path: /usr/bin/xcodebuild Languages: Java: version: 21.0.2 path: /usr/bin/javac Ruby: version: 2.6.10 path: /usr/bin/ruby npmPackages: "@react-native-community/cli": Not Found react: installed: 18.2.0 wanted: 18.2.0 react-native: installed: 0.74.1 wanted: 0.74.1 react-native-macos: Not Found npmGlobalPackages: "react-native": Not Found Android: hermesEnabled: true newArchEnabled: false iOS: hermesEnabled: true newArchEnabled: false

H3tansh avatar May 31 '24 07:05 H3tansh

Same issue here..

const fileExtension = coverImage.split(".").pop();

  // Create a new FormData instance
  const formData = new FormData();

  formData.append("image", {
    uri: coverImage,
    name: "image-" + new Date() + `.${fileExtension}`,
    type: `image/${fileExtension}`,
  } as any);

  // Append other form fields to FormData
  formData.append("food_name", data.food_name);
  formData.append("description", data.description);
  formData.append("price", String(data.price));
  formData.append("category", data.category);
  formData.append("addOn", JSON.stringify(data.addon));

  // Append sizes if applicable
  if (isSize && data.size.length > 0) {
    data.size.forEach((input, index) => {
      formData.append(`size[${index}]`, input.size);
      formData.append(`size_price[${index}]`, String(input.price));
    });
  }

  const response = await axios.post(
    `${app_url}/addMenuItem/${resId}`,
    formData,
    {
      headers: {
        "Content-Type": "multipart/form-data",
      },
    }
  );

When I console.log req.file and req.body,

undefined [Object: null prototype] { food_name: '', description: '', price: 'null', category: '', addOn: '{"addon":"","price":0}' }

Here I'm not sending any data in req.body only sending file to req.file which I get undefined.. This works fine with react native 0.73.x but not with 0.74.x

saadkhan2211 avatar Jun 06 '24 08:06 saadkhan2211

This issue is waiting for author's feedback since 24 days. Please provide the requested feedback or this will be closed in 7 days.

github-actions[bot] avatar Jul 01 '24 05:07 github-actions[bot]

Same problem here yet

felipeavila-vp avatar Jul 05 '24 12:07 felipeavila-vp

@saadkhan2211 @felipeavila-vp @H3tansh Hi, could you provide Missing Reproducible Example as above, so I can help you solve this problem?

huzhanbo1996 avatar Jul 11 '24 12:07 huzhanbo1996

@huzhanbo1996  below is my working code, It only works if I use deployed HTTPS API URL , but do not work if I use HTTP endpoint running on my localhost.

 const formData = new FormData()


    formData.append('file', {
        uri: path,
        name: fileName, // 'sound.mp3'
        type: fileType // 'audio/mp3'
    })

const response = await fetch(
    'https://dummy.url.com/api,
    {
        method: 'POST',
        headers: {
            'Content-Type': 'multipart/form-data',
            Authorization: `Bearer ${TOKEN}`
        },
        body: formData
    }
)

P.S:- Issue only persist in android.

here is the issue I've created https://github.com/facebook/react-native/issues/44737

H3tansh avatar Jul 12 '24 04:07 H3tansh

Same here, but with windows. It appears to be a problem only with multipart/form-data. I got the same code running on all platforms, but windows is the only one causing trouble, and only with this method :

static async postFormData<T>(
    endpoint: string,
    data: any = {},
    token?: string,
  ): Promise<T> {
    try {
      const url = `${API_BASE_URL}/${endpoint}`;

      const headers: Record<string, string> = {
        'Content-Type': 'multipart/form-data',
      };

      // Include token in headers if provided
      if (token) {
        headers['Authorization'] = `Bearer ${token}`;
      }

      const response = await fetch(url, {
        method: HttpMethod.POST,
        headers,
        body: data,
      });

      if (!response.ok) {
        let errorMessage = `HTTP error! Status: ${response.status}`;
        const contentType = response.headers.get('content-type');
        if (contentType && contentType.includes('application/json')) {
          const responseData = await response.json();
          if (responseData && responseData.reason) {
            errorMessage = responseData.reason;
          }
        }
        throw new Error(errorMessage);
      }

      return (await response.json()) as T;
    } catch (error) {
      throw error;
    }
  }

raphpay avatar Oct 24 '24 12:10 raphpay

Hi Everyone, Mine got fixed after some troubleshooting...All I did was that I removed Content-Type part from headers and It worked

saadkhan2211 avatar Oct 24 '24 12:10 saadkhan2211

I had the same problem after upgrade React Native to 0.72.17. And in my case renamed from Content-Type => content-Type fixed the error.

jocoders avatar Nov 05 '24 10:11 jocoders

same issue,

sssajjad007 avatar Nov 17 '24 17:11 sssajjad007

Same issue.

GalMarkelj avatar Nov 28 '24 12:11 GalMarkelj

Same issue

adriano461997 avatar Dec 07 '24 23:12 adriano461997

Same issue, its only happening in android, iOS is fine.

falmudayfir avatar Dec 12 '24 09:12 falmudayfir

i have same issue

Tiepbm avatar Dec 19 '24 08:12 Tiepbm

I had the same problem after upgrade React Native to 0.72.17. And in my case renamed from Content-Type => content-Type fixed the error.

not work for me =(

felipeavila-vp avatar Dec 19 '24 18:12 felipeavila-vp

Hi Everyone, Mine got fixed after some troubleshooting...All I did was that I removed Content-Type part from headers and It worked

not work here =(

felipeavila-vp avatar Dec 19 '24 18:12 felipeavila-vp

In my case it was the timeout. I defined the timeout to 3000 and takes more than that to upload file. So I just increased the timeout or even can remove if you don't need to control the timeout.

adriano461997 avatar Dec 19 '24 23:12 adriano461997

The problem is buried down into com.facebook.react.modules.network.NetworkingModule.constructMultipartBody(...) method. It expects to find Content-type header in the FormData part, containing the URI to the file. Something like this:

const part = {
    headers: [ ['content-type', 'image/png'] ],
    uri: 'file:///...',
    name: fileName,
    mimeType: 'image/png'
}

If there is no Content-type header, even though mimeType for the part is provided, the method returns null (line 725 in version 0.76.5) which causes the entire request to go silently into the trash, causing this nice and informative "Network error"

Even better - if you explicitly specify headers property in your FormData part it will not help you, because somewhere in the transit between the layers, the headers are overwritten mercilessly with content-disposition header. Thus, no matter what you provide in your request, only the content-disposition header reaches the NetworkingModule.

I was able to workaround the problem by registering custom RequestBodyHandler with the NetworkingModule and re-routing the processing of "form-data" payload to it, but it was ugly.

simeon-kirov avatar Dec 24 '24 15:12 simeon-kirov

I was able to workaround the problem by registering custom RequestBodyHandler with the NetworkingModule and re-routing the processing of "form-data" payload to it, but it was ugly.

@simeon-kirov Hi, I think I have encountered the same problem as you. When I use HTTPS to send formData, it always returns 403, and the request does not reach Nginx. Can you share your solution steps?

bestchenyan avatar Dec 25 '24 14:12 bestchenyan

Hi @bestchenyan,

You can find example workaround implementation here: https://github.com/simeon-kirov/expo-debug/tree/main. I tried to describe what's done in the README and inline JavaDoc.

However, you're saying in your case it always returns HTTP error 403. This is different from what I observe. In my case I'm getting just "Network error" message without any HTTP code. Probably your problem is related to self-signed/untrusted SSL certificates on the server. Make sure that you've registerd your server certificates as trusted. Check this documentation: https://developer.android.com/privacy-and-security/security-config. Also, you can see the configuration in my project:

  • android/app/src/debug/AndroidManifest.xml
  • android/app/src/debug/res/xml/network_security_config.xml
  • android/app/src/debug/res/raw/trusted_certificates

simeon-kirov avatar Dec 26 '24 13:12 simeon-kirov

Same issue

zhenyiplusone avatar Dec 27 '24 09:12 zhenyiplusone

I managed to fix it on Android by just setting 'Content-Type': 'multipart/form-data' in headers. it seems this is not required on iOS.

 axios.post('http://your-server.com/upload', formData, {
       headers: {
         'Content-Type': 'multipart/form-data'
       }
 })

fadi-quader-mox avatar Dec 31 '24 03:12 fadi-quader-mox

Having the same problem, only on Android as well. Using Axios worked

zwenza avatar Jan 24 '25 14:01 zwenza

@zwenza Could you share your code?

mandeepBartwal1079 avatar Jan 27 '25 11:01 mandeepBartwal1079

Having the same problem, only on Android as well. Using Axios worked

Here not work using axios or fetch. =(

felipeavila-vp avatar Jan 27 '25 11:01 felipeavila-vp

I managed to fix it on Android by just setting 'Content-Type': 'multipart/form-data' in headers. it seems this is not required on iOS.

axios.post('http://your-server.com/upload', formData, { headers: { 'Content-Type': 'multipart/form-data' } })

Hello friend! I tryied this, but the same problem occurs

felipeavila-vp avatar Jan 27 '25 11:01 felipeavila-vp

async function handleSubmit() { try { const formData = new FormData(); console.log('Submit button pressed');

  formData.append('inventoryOrderItems.OrderId', order.orderId.toString());

  productList.forEach((product, index) => {
    const baseKey = `inventoryOrderItems.OrderItemDatas[${index}]`;
    formData.append(`${baseKey}.Id`, product.id.toString());
    formData.append(`${baseKey}.ProductId`, product.productId.toString());
    formData.append(`${baseKey}.RecievingQty`, (inputValues[product.productCode] || 0).toString());

    const images = imagesByProductCode[product.productCode] || [];
    images.forEach((imageUri: string) => {
      if (!imageUri.startsWith('file://')) {
        throw new Error(`Invalid image URI: ${imageUri}`);
      }

      const fileName = imageUri.split('/').pop() || 'unknown.jpg';
      const fileType = `image/${fileName.split('.').pop()}`; // Example: 'image/jpeg'

      // Append file object to FormData
      formData.append(`${baseKey}.files`, {
        uri: imageUri,
        name: fileName,
        type: fileType,
      } as any); // Cast as any to bypass TypeScript errors if any
    });
  });

  // Debug FormData (for React Native)
  for (const pair of (formData as any)._parts) {
    console.log(pair[0], pair[1]);
  }

  const response = await axios.post(`MYAPIURL`, formData, {
    headers: {
      accept: 'application/json',
      'content-type': 'multipart/form-data',
    },
    maxBodyLength: Infinity,
  });

  if (response.data.success) {
    const _routeId: string | number = order?.orderRouteId;
    console.log('Data submitted successfully:', response.data);
    Alert.alert('Order details submitted successfully!');
    navigation.navigate('ORDERSDETAIL', {_routeId});
  } else {
    console.log('Submission failed:', response.data);
    Alert.alert('Submission failed.');
  }
} catch (error: any) {
  console.error('Error:', error.message);

  if (error.response) {
    console.error('Error response data:', error.response.data);
    Alert.alert(error.response.data.message || 'Unable to submit data.');
  } else {
    Alert.alert('Network error. Please try again.');
  }
}

}

This is my code; sometimes it works, and sometimes it doesn't. Can someone please help?

mandeepBartwal1079 avatar Jan 27 '25 11:01 mandeepBartwal1079

same issue. Please advice if there is any other ways to POST or PUT form data or file properly with current react native version

angus6b23 avatar Feb 06 '25 18:02 angus6b23

same issue, any found solution?

ganeshmohane avatar Feb 09 '25 09:02 ganeshmohane