typescriptreact-nativeexpofetch-api

React Native blob/file is not getting to the server


I am extremely stuck here. I don't know what I'm doing wrong. I'm trying to send a file from the an expo-image-picker component to the server. The form is sent, but the image is not. The fetch command immediately gives the "Network request failed" error. The server DOES receive the request, but no image is attached.

More information:

Here are the functions I'm using to do the submit. pathToImage is returned from the ImagePicker.

const fetchImageFromUri = async (uri: string) => {
  try {
    const response = await fetch(uri);
    const blob = await response.blob();

    return blob;
  } catch (error) {
    console.log("fetchImageFromUri error:", error);
    throw new Error("fetchImageFromUri");
  }
};

const upload = async () => {
  setMessage("");
  setErrMessage("");

  if (pathToImage != null) {
    const fileToUpload = await fetchImageFromUri(pathToImage);

    const formData = new FormData();
    formData.append("action", "Image Upload");
    formData.append("image", fileToUpload, "filename");

    // from: https://stackoverflow.com/questions/71198201/react-native-unable-to-upload-file-to-server-network-request-failed
    // most articles say this is the way to upload the file... Typescript gives an error
    // because it only wants type 'string | Blob'
    // let uriParts = pathToImage.split(".");
    // let fileType = uriParts[uriParts.length - 1];
    // formData.append("image", {
    //   uri: pathToImage,
    //   name: `photo.${fileType}`,
    //   type: `image/${fileType}`,
    // });

    // create the header options
    const options: RequestInit = {
      method: "POST",
      body: formData,
      headers: {
        "Content-Type": "multipart/form-data",
        Accept: "image/jpeg, image/png",
      },
    };

    try {
      const res = await fetch(URL, options);
            
      console.log("fetch returned"); // this line is never reached
            
      if (!res.ok) {
        throw new Error("Something went wrong");
      }

      const body = (await res.json()) as any;
            
      if (body.code > 200) {
        setErrMessage(body.msg);
      } else {
        setMessage(body.msg);
      }
    } catch (err: any) {
      setErrMessage("There was an error in upload");
      console.log("upload catch error:", err.message);
    }
  }
};

The full code can be found in my GitHub repository.


Solution

  • Thanks to Brandonjgs for pointing me in the right direction. I was able to solve the problem.

    Here is the new upload function.

    const upload = async () => {
        console.log("\n***** Upload Image *****");
        setMessage("");
        setErrMessage("");
        setLoading(true);
    
        if (pathToImage) {
            console.log("***** get other fields section *****");
            const dataToSend: Record<string, string> = {};
            dataToSend["action"] = "Image Upload";
    
            console.log("***** Options section *****");
            const options: FileSystemUploadOptions = {
                headers: {
                    "Content-Type": "multipart/form-data",
                    Accept: "image/jpeg, image/png",
                },
                httpMethod: "POST",
                uploadType: FileSystemUploadType.MULTIPART,
                fieldName: "image",
                parameters: dataToSend,
            };
    
            console.log("***** 'Fetch' section *****");
            try {
                const response = await FileSystem.uploadAsync(
                    URL,
                    pathToImage,
                    options
                );
    
                setLoading(false);
    
                if (response.status >= 200 && response.status < 300) {
                    const body = JSON.parse(response.body);
                    setMessage(body.msg);
                } else {
                    setErrMessage(`${response.status} Error: ${response.body}`);
                }
            } catch (err: any) {
                console.error(err);
                setErrMessage(err.message);
            }
        }
    };
    

    Be sure to look at the FileSystem.uploadAsync documentation. It doesn't return a standard http response, it formats its own and just returns: