Home > Mobile >  Axios POST request sending nothing with 'multipart/form-data' [React Native - Expo]
Axios POST request sending nothing with 'multipart/form-data' [React Native - Expo]

Time:03-03

Alright I've been bobbling over this issue for the entire day. Here are the details of my issue:

Scenario

Front end is basically a React Native (Expo) application where users can issue a report - this includes taking multiple photos and filling in some details.

Backend is just node.js, with Express & Multer.

Problem

I use Axios to send the images and form data through FormData(), however on the server side, req.body and req.files consist of nothing.

One thing here is that sending the SAME data through POSTMAN works COMPLETELY fine, the images were stored into S3 and the form details were stored in the database. It is through the app/emulator that does not work.

I've tried removing the "multipart/form-data" header and this is the output from console.log(req.body) (req.files show undefined):

{
  _parts: [
    [ 'userId', '1f400069-07d0-4277-a875-cbb2807750c5' ],
    [
      'location',
      'some location'
    ],
    [ 'description', 'Aaaaa' ],
    [ 'report-images', [Object] ]
  ]
}

When I put the "multipart/form-data" header back this output didn't even bother showing up.

What I've Done

I've been searching for solutions for the past hours and none of them worked. Those solutions are:

  1. Adding boundary behind the "multipart/form-data" header
  2. Putting the type to "image/jpeg"
  3. Trimming the file uri to "file://"

Yet none of them works

Here is my code:

React Native Frontend (Expo)

   const submitReport = async () => {
    setShowLoading(true);

    // Validate details (location & images)
    if (location === "") {
        setShowLoading(false);
        showToast(7002);
        return;
    }

    if (images.length === 0) {
        setShowLoading(false);
        showToast(7004);
        return;
    }

    try {
        const formData = new FormData();
        formData.append("userId", user.userId);
        formData.append("location", location);
        formData.append("description", description);
        images.forEach(img => {
            const trimmedURI = (Platform.OS === "android") ? img.uri : img.uri.replace("file://", "");
            const fileName = trimmedURI.split("/").pop();
            const media = {
                name: fileName,
                height: img.height,
                width: img.width,
                type: mime.getType(trimmedURI),
                uri: trimmedURI
            };

            formData.append("report-images", media);
        });

        const response = await axios.post(`http://<my-ip-address>:3003/api/report/submit`, formData, { headers: { 'Content-Type': "application/x-www-form-urlencoded" } });
        console.log(response)
        setShowLoading(false);
    }
    catch (error) {
        console.log(error);
        setShowLoading(false);
        showToast(9999);
    }
};

Backend

// Multer-S3 Configuration
const upload = multer({
    storage: multerS3({
        s3: s3,
        bucket: process.env.AWS_S3_BUCKET_NAME,
        contentType: (req, file, callback) => {
            callback(null, file.mimetype);
        },
        metadata: (req, file, callback) => {
            callback(null, { fieldName: file.fieldname });
        },
        key: (req, file, callback) => {
            callback(null, `${process.env.AWS_S3_REPORT_IMAGES_OBJECT_PATH}${req.body.userId}/${new Date().getTime().toString()}-${file.originalname}`)
        }
    }),
    fileFilter: (req, file, callback) => {
        // Check if file formats are valid
        if (file.mimetype === "image/png" || file.mimetype === "image/jpg" || file.mimetype === "image/jpeg") {
            callback(null, true);
        }
        else {
            callback(null, false);
            return callback(new Error("Image File Type Unsupported"));
        }
    },
});

router.post("/submit", upload.array("report-images", 3), async (req, res) => {
    try {
        // req -> FormData consisting of userId, location & description
        // multer-s3 library will handle the uploading to S3 - no need to code here
        // Details of files uploaded on S3 (Bucket, Key, etc.) will be displayed in req.files
        
        // Analyze from Rekognition
        //Add to Database code blablabla

        if (result.success === true) {
            res.status(200).send({ message: result.data });
        }
        else if (result.success === false) {
            res.status(404).send({ error: ERROR_MESSAGE });
        }
    }
    catch (error) {
        console.log(error);
        res.status(404).send({ error: ERROR_MESSAGE });
    }
});

I'm unsure if this an axios problem or some problem on my side that's why I'm posting here.

Feel free to ask if clarifications are needed.

If anyone sees this and have experience, please provide me some workarounds to solve this issue, as this project is my Final Year Project and its really urgent. Thanks.

CodePudding user response:

You need to change your image uploading code with this one, you also need to install mime npm package.

const formData = new FormData();
formData.append("userId", user.userId);
formData.append("location", location);
formData.append("description", description);

const formData = new FormData();
files = files || [];
if (files.length) {
    for (let index = 0; index < files.length; index  ) {
        const filePayload = files[index];
        const file = filePayload.value;
        const localUri =
            Platform.OS === "android" ?
            file.uri :
            file.uri.replace("file://", "");
        const newImageUri = localUri;
        const filename = newImageUri.split("/").pop();

        const media = {
            name: filename,
            height: file?.height,
            width: file?.width,
            type: mime.getType(newImageUri),
            uri: localUri,
        };
        formData.append(filePayload.name, media);
    }
}

const response = await axios.post(`http://<my-ip-address>:3003/api/report/submit`, formData, {
    headers: headers: {
        "Content-Type": "application/x-www-form-urlencoded",
    },
});

CodePudding user response:

So after diving through search results in Google, I've found this StackOverflow post: react native post form data with object and file in it using axios

I took the answer provided by user_2738046 in my code and it worked! Combining with Ali's suggestion here is the final code that worked.

const FormData = global.FormData;
const formData = new FormData();
formData.append("userId", user.userId);
formData.append("location", location);
formData.append("description", description);
images.forEach(img => {
    const trimmedURI = (Platform.OS === "android") ? img.uri : img.uri.replace("file://", "");
    const fileName = trimmedURI.split("/").pop();
    const media = {
        name: fileName,
        height: img.height,
        width: img.width,
        type: mime.getType(trimmedURI),
        uri: trimmedURI
    };

    formData.append("report-images", media);
});
            
const response = await axios({
    method: "POST",
    url: `http://${<my-ip-address>}:3003/api/report/submit`,
    data: formData,
    headers: {
        'Content-Type': 'multipart/form-data'
    },
    transformRequest: (data, error) => {
        return formData;
    }
});

// If success, clear all text fields
if (response) {
    showToast(7005);
    setLocation("");
    setImages([]);
    setDescription("");
}

setShowLoading(false);
  • Related