Reactjs 如何使用Multer在MERN Stack应用程序中上传图像文件

Reactjs 如何使用Multer在MERN Stack应用程序中上传图像文件,reactjs,mongodb,express,multer,mern,Reactjs,Mongodb,Express,Multer,Mern,我正试图在我的项目中使用multer将图像上传为文件,但我不知道为什么在控制台中的req.files:[]上看到onCreate request in backend;(空数组) 这里是我创建元素的地方: const [imagesColors, setImagesColors] = useState([{image: [], color: ''}]) const createProduct = (e) => { e.preventDefault

我正试图在我的项目中使用multer将图像上传为文件,但我不知道为什么在控制台中的req.files:[]上看到onCreate request in backend;(空数组)

这里是我创建元素的地方:

    const [imagesColors, setImagesColors] = useState([{image: [], color: ''}])
    
    const createProduct = (e) => {
        e.preventDefault ();
    
        const data = new FormData()
    
    data.append("name", name)
        data.append("description", description)
        data.append("processor", processor)
        data.append("ram", ram)
        data.append("storage", storage)
        data.append("imagesColors", imagesColors)
        data.append("price", price)
        data.append("type", type)
    
        console.log(data)
    
        fetch ('http://localhost:5000/products/create', {
          method: 'POST',
          body: data
        })...
    
    const handleInputChangeColor = (e, index) => {
        const { name, value } = e.target;
        const list = [...imagesColors];
        list[index][name] = value;
        setImagesColors(list);
      };
    
      const handleInputChangeImage = (e, index) => {
        const name = e.target.name;
        const file = e.target.files;
        const list = [...imagesColors];
        list[index][name] = file;
        setImagesColors(list);
      };
    
return (
...
    {imagesColors.map((x, i) => {
                        return (
                          <div className="box">
                            <label htmlFor="file" className="file--Input--Container">
                              <input
                                type="file"
                                id="file"
                                multiple
                                name="image"
                                className="file--Input"
                                filename="imageFile"
                                placeholder="Product Image"
                                onChange={e => handleInputChangeImage(e, i)}
                              />
                              <div className="file--Label--Container">
                                 <FaCloudUploadAlt className="upload--Icon"/> Upload Images
                              </div>
                            </label>
                            <select
                              onChange={e => handleInputChangeColor(e, i)}
                              value={x.color}
                              name="color"
                              defaultValue=""
                            >
                              <option selected value="">Color</option>
                              <option value='#4f5b66'>Space-gray</option>
                              <option value='#a7adba'>Silver</option>
                              <option value='#FFFFFF'>White</option>
                              <option value='#F63204'>Red</option>
                              <option value='#000000'>Black</option>
                              <option value='#0095CB'>Pacific-Blue</option>
                            </select>
                            <div className="btn-box">
                              {imagesColors.length !== 1 && <button onClick={() => handleRemoveClick(i)}>Remove</button>}
                              {imagesColors.length - 1 === i && <button onClick={handleAddClick}>Add</button>}
                            </div>
                          </div>
                        );
                      })}
以及控制台中来自请求的结果:


您正在以对象数组(不是文件数组)的形式发送
imageColors
,其中包括图像文件列表和颜色。此外,您还需要在表单数据键的末尾添加括号,否则它将无法正确解析

您可以在发送之前映射ImageColor对象:

data.append(“imagesColors[]”,imagesColors.map(i)=>i.images[0])
如果您还想发送颜色名称,则必须将其发送到另一个数组中:

data.append(“colors[]”,imagesColors.map(i)=>i.color)

提出此问题的人接受任何解决问题的方法(参见此问题的注释)。我用一种不使用multer之类的包来上传文件的方法来回答

因此,方法是首先在前端将图像转换为base64并将其发送到后端,在后端将base64转换回文件

下面是将文件转换为base64的前端代码

传递的参数
images
只不过是
event.target.files

const [imagesArray,setImagesArray]=useState()

let imageToBase64=(images)=>{  //passed parameter images here
    let imagesBase64=[]
    for(let image of images){
      const reader = new FileReader();

    reader.onload = () => {
      if (reader.readyState === 2) {
        let imageBase64=reader.result
        imagesBase64.push(imageBase64)
      }
    };
    reader.readAsDataURL(image);
  }
  setImagesArray(imagesBase64);
  }
将前端设置为按以下格式发送数据

{
    name: req.body.name,
    description: req.body.description,
    processor: req.body.processor,
    ram: req.body.ram,
    storage: req.body.storage,
    imagesColors: [base64 string,base64 string],//differnce here
    price: req.body.price,
    type: req.body.type,
    likes: req.body.likes
}
后端代码

将base64转换为文件(我相信您正在上载图像,所以我使用了sharp软件包)

将此行放入根文件(可能是index.js或app.js)
app.use(express.json({limit:'50mb'}))


要使用FormData API在前端发送多个文件,必须逐个追加这些文件。您可以将它们附加到同一字段,该字段将作为后端的数组到达。如果要随每个文件一起发送附加数据,请按相同顺序将该数据附加到不同字段

在您的情况下,它看起来是这样的:

用于(常量图像和图像颜色){
data.append('images',imageAndColor.image);
data.append('colors',imageAndColor.color);
}

在后端,将uploads.array(“imagesColors”)更改为uploads.array(“images”)。您的图像将位于
req.files
中,颜色位于
req.body.colors
中。两个数组的顺序都是有保证的-第一个映像是两个数组中的第一个元素,第二个映像是第二个元素,依此类推。

只需查看req.imagesColors日志。@NithinKJoy[object]。但是我如何解决它呢?我在两天前用multer上传(文件和json数据)时遇到了同样的问题。因此,我所做的是在前端将图像转换为base64并将其上载,在后端将base64转换为文件。你需要那个源代码吗?@NithinKJoy是的,你能提供吗?所以没有办法用组合对象发送它们?也许你知道我为什么要用组合对象来发送?所以我可以在前端做一个变体。实际上,你可以在附加它们之前用颜色改变文件名
imagesColors.forEach(i=>data.append(“imagesColors[]”,i.images[0],i.color)
.Last
i.color
参数将替换文件名。我收到多个错误:
MulterError:wrappedFileFilter(\Multer\index.js:40:19)处的意外字段
为什么我会出现这个错误?谢谢你,伙计。这正是我想要的。差不多,但我认为这个订单将在前端对我有很大帮助
{
    name: req.body.name,
    description: req.body.description,
    processor: req.body.processor,
    ram: req.body.ram,
    storage: req.body.storage,
    imagesColors: [base64 string,base64 string],//differnce here
    price: req.body.price,
    type: req.body.type,
    likes: req.body.likes
}
const sharp = require("sharp");
const FileType = require("file-type");


router.post('/create', async (req, res) => {
    
 async function convert(base64) {
  var buffer = Buffer.from(base64.split(";base64,").pop(), "base64");
  let {ext} = await FileType.fromBuffer(buffer);
  let newPath =
    `/public/any path/` +
    Date.now() +
    Math.random().toString().slice(2, 14) +
    "." +
    ext;

  await sharp(buffer)
    .resize(1920, 1080)
    .toFile(newPath)
    .catch(err => console.log(err));

   return "done"
};

//this converts all base64 to images.
//warning: never `console.log(req.body.imagesColors)` because base64 is very very long string and logging that can crash your terminal.

if (req.body.imagesColors.length > 0)
    for (let image of req.body.imagesColors)
      await convert(image)
})