Axios POST 请求不发送 'multipart/form-data' [React Native - Expo]

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

场景

前端基本上是一个 React Native (Expo) 应用程序,用户可以在其中发布报告 - 这包括拍摄多张照片并填写一些细节。

后端只是 node.js,带有 Express 和 Multer。

问题

我使用Axios通过FormData()发送图片和表单数据,但是在服务器端,req.body和req.files什么都没有.

这里的一件事是,通过 POSTMAN 发送相同的数据完全正常,图像存储在 S3 中,表单详细信息存储在数据库中。就是通过app/emulator不行。

我试过删除“multipart/form-data”header,这是 console.log(req.body) (req.files 显示未定义):

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

当我把“multipart/form-data”header放回去时,这个输出甚至都懒得显示了。

我做了什么

过去几个小时我一直在寻找解决方案,其中 none 有效。这些解决方案是:

  1. 在“multipart/form-data”后面添加边界header
  2. 正在将类型设置为“image/jpeg”
  3. 将文件 uri 修剪为“file://”

然而 none 有效

这是我的代码:

React Native 前端(博览会)

   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);
    }
};

后端

// 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 });
    }
});

我不确定这是 Axios 问题还是我这边的问题。

这个项目是我的毕业设计。

你需要用这个修改你的图片上传代码,你还需要安装mime npm包。

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",
    },
});

因此,在 Google 中深入搜索结果后,我发现了这个 Whosebug post:

我在我的代码中采用了 user_2738046 提供的答案,并且有效!在这里结合 Ali 的建议是最终有效的代码。

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);