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:
I am creating the form-data object and append the blob to it. I've also tried doing it with FormData.append("image", {uri, name: 'filename', type: 'image/filetype'})
, the way most articles suggest, ignoring the TS error but it fails as well.
I'm not submitting to AWS or Firebase, so I'm not using those libraries, I don't see what they are doing any different from me in any case.
I haven't set any specific permissions for this. I did see some articles talking about permissions for uploading, but they were over 5 years old and talking about before android 5.0.
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.
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:
status
: the error codeheader
: the http header returned from the serverbody
: whatever the server "sends" back - in my case it is JSON. Note, that if the server is unresponsive or it is a bad URL, it returns an HTML error page, not a standard error message string (which I find an odd choice since this is react native and I'm not necessarily navigating to a new page when I do an upload, I capture the string in a state object to post the response).