admin管理员组

文章数量:1122832

I am developing a React Native (Expo) application where users can upload files with descriptions. However, I am encountering issues when trying to send the files to the server via the fetch API using FormData. The server responds with errors or fails to receive the file and description data properly.

What I Want to Achieve: Allow users to pick files (using expo-document-picker) and optionally add descriptions for each file. Send the files and their descriptions as a POST request to the server using FormData. Ensure that files are properly processed on the server and the upload succeeds. Current Code: Here’s the relevant part of my implementation:

I use DocumentPicker to select files and store them in the files state. I construct a FormData object, appending each file and its description. I send this data using the fetch API.

    if (!files || files.length === 0) {
      alert('No files selected. Please select at least one file to upload.');
      return;
    }
  
    if (Object.keys(descriptions).length !== files.length) {
      alert('Each file must have a corresponding description.');
      return;
    }
  
    setLoading(true);
  
    try {
      const formData = new FormData();
  
      files.forEach((file, index) => {
        formData.append('files', {
          uri: file.uri.startsWith('file://') ? file.uri : `file://${file.uri}`, // Ensure proper URI
          name: file.name || `file_${index}`, // Assign name
          type: file.type || 'application/octet-stream', // Default MIME type
        });
  
        formData.append('descriptions', descriptions[index] || ''); // Append descriptions
      });
  
      // Debugging FormData
      console.log('FormData before sending:');
      for (const pair of formData.entries()) {
        console.log(pair[0], pair[1]);
      }
  
      // API Request
      const response = await fetch(
        `${process.env.EXPO_PUBLIC_API_URL}/v1/assets/assignments/${assignment_id}`,
        {
          method: 'POST',
          headers: {
            'Authorization': `Bearer ${user.token}`, // Add auth token only
          },
          body: formData, // FormData automatically includes the boundary
        }
      );
  
      if (!response.ok) {
        const errorText = await response.text();
        console.error('Backend Response Error:', errorText);
        alert(`Error from server: ${errorText}`);
        return;
      }
  
      alert('Files uploaded successfully!');
      navigation.goBack();
    } catch (error) {
      console.error('Error uploading files:', error);
      alert(`Error uploading files: ${error.message}`);
    } finally {
      setLoading(false);
    }
  };

Problem:

The files are either not received by the server or the fetch API fails with an error. Sometimes, the server reports missing or corrupted file data. The FormData object doesn't appear to work as expected.

Debugging Steps Taken: Verified that the DocumentPicker is correctly returning file metadata (e.g., uri, name, type). Used console.log to confirm that FormData is being populated with the files and descriptions. Tried adding the Content-Type header manually (e.g., multipart/form-data), but it caused issues because fetch sets this header automatically for FormData.

Environment Details: Expo SDK: ~51.0.28 React Native: 0.74.5 Backend: Node.js/Express

Questions: What is the correct way to upload files using fetch and FormData in React Native (Expo)? Is there a specific issue with file URIs (e.g., file:// or base64) that I need to address? How can I ensure that the server properly receives both the file data and the descriptions?

I am developing a React Native (Expo) application where users can upload files with descriptions. However, I am encountering issues when trying to send the files to the server via the fetch API using FormData. The server responds with errors or fails to receive the file and description data properly.

What I Want to Achieve: Allow users to pick files (using expo-document-picker) and optionally add descriptions for each file. Send the files and their descriptions as a POST request to the server using FormData. Ensure that files are properly processed on the server and the upload succeeds. Current Code: Here’s the relevant part of my implementation:

I use DocumentPicker to select files and store them in the files state. I construct a FormData object, appending each file and its description. I send this data using the fetch API.

    if (!files || files.length === 0) {
      alert('No files selected. Please select at least one file to upload.');
      return;
    }
  
    if (Object.keys(descriptions).length !== files.length) {
      alert('Each file must have a corresponding description.');
      return;
    }
  
    setLoading(true);
  
    try {
      const formData = new FormData();
  
      files.forEach((file, index) => {
        formData.append('files', {
          uri: file.uri.startsWith('file://') ? file.uri : `file://${file.uri}`, // Ensure proper URI
          name: file.name || `file_${index}`, // Assign name
          type: file.type || 'application/octet-stream', // Default MIME type
        });
  
        formData.append('descriptions', descriptions[index] || ''); // Append descriptions
      });
  
      // Debugging FormData
      console.log('FormData before sending:');
      for (const pair of formData.entries()) {
        console.log(pair[0], pair[1]);
      }
  
      // API Request
      const response = await fetch(
        `${process.env.EXPO_PUBLIC_API_URL}/v1/assets/assignments/${assignment_id}`,
        {
          method: 'POST',
          headers: {
            'Authorization': `Bearer ${user.token}`, // Add auth token only
          },
          body: formData, // FormData automatically includes the boundary
        }
      );
  
      if (!response.ok) {
        const errorText = await response.text();
        console.error('Backend Response Error:', errorText);
        alert(`Error from server: ${errorText}`);
        return;
      }
  
      alert('Files uploaded successfully!');
      navigation.goBack();
    } catch (error) {
      console.error('Error uploading files:', error);
      alert(`Error uploading files: ${error.message}`);
    } finally {
      setLoading(false);
    }
  };

Problem:

The files are either not received by the server or the fetch API fails with an error. Sometimes, the server reports missing or corrupted file data. The FormData object doesn't appear to work as expected.

Debugging Steps Taken: Verified that the DocumentPicker is correctly returning file metadata (e.g., uri, name, type). Used console.log to confirm that FormData is being populated with the files and descriptions. Tried adding the Content-Type header manually (e.g., multipart/form-data), but it caused issues because fetch sets this header automatically for FormData.

Environment Details: Expo SDK: ~51.0.28 React Native: 0.74.5 Backend: Node.js/Express

Questions: What is the correct way to upload files using fetch and FormData in React Native (Expo)? Is there a specific issue with file URIs (e.g., file:// or base64) that I need to address? How can I ensure that the server properly receives both the file data and the descriptions?

Share Improve this question asked Nov 21, 2024 at 11:57 Abdella MunirAbdella Munir 11 bronze badge
Add a comment  | 

1 Answer 1

Reset to default 0

From your question, you are trying to upload multiple files. First, your API must have been confirgured to process multiple entries of both the file and description keys. However, in your code, you are sending file, instead of file[]. In your code, assuming you had earlier set some state variables, change your code from this:

files.forEach((file, index) => {
        formData.append('files', {
          uri: file.uri.startsWith('file://') ? file.uri : `file://${file.uri}`, // Ensure proper URI
          name: file.name || `file_${index}`, // Assign name
          type: file.type || 'application/octet-stream', // Default MIME type
        });
  
        formData.append('descriptions', descriptions[index] || ''); // Append descriptions
      });

to this:

files.forEach((file, index) => {
        formData.append('files[]', {
          uri: file.uri,
          name: file.name,
          type: file.type || 'application/octet-stream', // Default MIME type
        });
  
        formData.append('descriptions[]', descriptions[index] || ''); // Append descriptions
      });

To further help you debug the codes, just do a console.log(formData) to see your data before upload. In your log, you will see something like below:

{"_parts": [["files[]", [Object]],["files[]", [Object]],["files[]", [Object]],["description[]", ["first description"]],["description[]", ["second description"]],["description[]", ["third description"]]]}

Hoping this helps.

本文标签: How to upload multiple files on React native ExpoStack Overflow