从我的前端发送FormData到后端不起作用(后端的req.body为空)

问题描述 投票:0回答:1

这是奥马尔,

注:话太多?如果您只是想了解问题,下面的部分并非全部都是必须检查的,只需查看以下部分:

  1. 前端(快速浏览一下代码)
  2. 后端(您可以检查是否需要,但这只是multer的标准设置)
  3. Postman(快速查看请求详细信息,不要查看其代码)
  4. 网络选项卡(检查它)
  5. 后端发生了什么(了解问题)

前端

我的问题是在我的前端(反应)中使用 fetch 发送

FormData
时:

const formData = new FormData();
formData.append('file', values.image);
formData.append('path', 'image');
formData.append('model', 'item');
formData.append(
  'data',
  JSON.stringify({
    menu: menu._id.toString(),
    item: values.item,
  })
);

const req = new Request(`/api/v1/upload`, {
  method: 'POST',
  body: formData,
});

Request
构造函数与fetch完全相同,但它只是每次都会跳过
response.json()
,Request类是我自己的(我构建的)

后端

后端代码使用 multer 处理多部分表单数据。 后端与邮递员完美配合,因此使用邮递员发送请求效果很好,但使用 fetch as up 则不行。 对于后端代码,很简单,我使用 upload.single 和内存存储。 代码:

const storage = multer.memoryStorage();

const upload = multer({ storage, fileFilter });

exports.processImage = (req, res, next) => {
  if (!req.file) return next();

  req.file.filename =
    req.modelController.createFileName(req, req.file) + '.jpeg';

  sharp(req.file.buffer)
    .toFormat('jpeg')
    .toFile(
      path.join(
        `public/image/upload/${req.body.model.toLowerCase()}`,
        req.file.filename
      )
    );

  next();
};

再次,如果我使用邮递员,效果很好

邮差

邮递员请求图片 Postman 代码与我的代码非常相似:

const formdata = new FormData();
formdata.append("model", "item");
formdata.append("path", "image");
formdata.append("data", "{\"menu\": \"673e442bae0dd4717f4af4c1\", \"item\": \"673f76230fb7a35ddfc2cd57\"}");
formdata.append("file", fileInput.files[0], "/C:/Users/LENOVO/Downloads/Matrix-Cola-Carbonated-Drink.jpg");

const requestOptions = {
  method: "POST",
  headers: myHeaders,
  body: formdata,
  redirect: "follow"
};

fetch("http://127.0.0.1:5423/api/v1/upload/", requestOptions)
  .then((response) => response.text())
  .then((result) => console.log(result))
  .catch((error) => console.error(error));

后端发生了什么?

即使使用 multer,它也会将主体接收为 null 对象,并且文件为未定义

网络选项卡

网络选项卡显示以下内容 网络选项卡,请求负载 标题进展顺利,内容类型是 multipart/form-data。

我尝试了什么?

我没有尝试很多事情,但是:

内容类型

设置内容类型标题是由表单数据实例自动完成的

什么是 Values.image?

它是[目标文件]

我使用什么形式

我使用formik来处理表单。 但我确信该文件保存在values.image

reactjs node.js fetch multipartform-data multer
1个回答
0
投票

使用 Request 的前端代码与 fetch 密切相关,因为 Request 对象被设计为与 Fetch API 一起使用。 Request 类本身并不执行实际的网络操作 - 它只是创建一个通常传递给 fetch 的 Request 对象。

出现问题的原因是您的后端在使用

fetch
时未正确接收多部分 FormData 负载。

该问题与 Fetch API 有关,因为您的自定义 Request 类可能会包装它。根本原因是 Content-Type 标头的处理以及 FormData 的传递方式。您可以通过确保正确使用 Request 类或 fetch 或切换到像 axios 这样的库以保持一致性来解决此问题。

axios.post
检查 FormData 并使用适当的边界设置 Content-Type 标头(多部分/表单数据格式的必需部分)。 这可确保服务器正确解释表单数据。

演示

server.js

const express = require('express');
const cors = require('cors');
const multer = require('multer');
const sharp = require('sharp');
const path = require('path');
const fs = require('fs');

// Initialize the Express app
const app = express();

// Middleware for JSON parsing and CORS
app.use(express.json());
app.use(cors());

// Configure multer storage in memory
const storage = multer.memoryStorage();
const upload = multer({ storage });

// Ensure upload directories exist
const ensureUploadDirExists = (dirPath) => {
  if (!fs.existsSync(dirPath)) {
    fs.mkdirSync(dirPath, { recursive: true });
  }
};

// POST route to handle file uploads
app.post('/api/v1/upload', upload.single('file'), async (req, res) => {
  try {
    if (!req.file) {
      return res.status(400).json({ error: 'No file uploaded.' });
    }

    const { path: uploadPath, model, data } = req.body;

    // Parse and log the additional data
    const parsedData = JSON.parse(data);
    console.log('Parsed data:', parsedData);

    // Ensure the directory exists
    const uploadDir = path.join(__dirname, 'public', uploadPath, model.toLowerCase());
    ensureUploadDirExists(uploadDir);

    // Create a unique filename
    const filename = `${Date.now()}-${model.toLowerCase()}.jpeg`;

    // Process and save the image
    await sharp(req.file.buffer)
      .toFormat('jpeg')
      .toFile(path.join(uploadDir, filename));

    // Respond with the file path and other details
    res.json({
      success: true,
      message: 'File uploaded successfully.',
      filePath: `/public/${uploadPath}/${model.toLowerCase()}/${filename}`,
      data: parsedData,
    });
  } catch (error) {
    console.error('Error processing upload:', error);
    res.status(500).json({ success: false, error: 'Failed to process upload.' });
  }
});

// Server listening
const PORT = process.env.PORT || 5423;
app.listen(PORT, () => {
  console.log(`Server running on port ${PORT}`);
});

服务器依赖安装

npm install express cors multer sharp

在前端

App.js

import React, { useState } from 'react';
import axios from 'axios';

const App = () => {
    const menu = { _id: '12345' };
    const [imageFile, setImageFile] = useState(null); // File input
    const [itemName, setItemName] = useState(''); // Item name input
    const [responseMessage, setResponseMessage] = useState('');

    // Handle file input change
    const handleFileChange = (event) => {
        setImageFile(event.target.files[0]);
    };

    const handleSubmit = async (event) => {
        event.preventDefault();

        if (!imageFile) {
            setResponseMessage('Please select an image to upload.');
            return;
        }

        const formData = new FormData();
        formData.append('file', imageFile);
        formData.append('path', 'image');
        formData.append('model', 'item');
        formData.append(
            'data',
            JSON.stringify({
                menu: menu._id.toString(),
                item: itemName,
            })
        );

        try {
            const response = await axios.post('http://localhost:5423/api/v1/upload', formData, {
                headers: { 'Content-Type': 'multipart/form-data' },
            });
            console.log(response.data);
            setResponseMessage(JSON.stringify(response.data, null, 2)); // Format JSON response
        } catch (error) {
            console.error('Error details:', error.response?.data || error.message);
            setResponseMessage('Error: Could not get a response.');
        }
    };

    return (
        <div>
            <form onSubmit={handleSubmit}>
                <div>
                    <label>
                        Upload Image:
                        <input type="file" onChange={handleFileChange} />
                    </label>
                </div>
                <div>
                    <label>
                        Item Name:
                        <input
                            type="text"
                            value={itemName}
                            onChange={(e) => setItemName(e.target.value)}
                        />
                    </label>
                </div>
                <button type="submit">Submit</button>
            </form>
            {responseMessage && (
                <div>
                    <h3>Server Response:</h3>
                    <pre>{responseMessage}</pre>
                </div>
            )}
        </div>
    );
};

export default App;

依赖安装

npm install axios

结果

node server.js
enter image description here

npm start
enter image description here

Postman
enter image description here

© www.soinside.com 2019 - 2024. All rights reserved.