如何使用 Multer 在 MERN Stack 应用程序中上传图像文件
How to upload image file in MERN Stack Application with Multer
我正在尝试在我的项目中使用 multer 将图像作为文件上传,但我不知道为什么我在 req.files 上看到的控制台后端中的 onCreate 请求:[]; (空数组)。
这是我创建元素的地方:
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>
);
})}
当我console.log状态imagesColors
它returns:
这里是创建函数,后端有多个函数:
const imageStorage = multer.diskStorage({
destination: (req, file, callback) => {
callback(null, "../../shpJS/frontend/src/styles/images")
},
filename: (req, file, callback) => {
callback(null, file.name)
}
})
const uploads = multer({storage: imageStorage})
router.post('/create', uploads.array("imagesColors"), async (req, res) => {
console.log(req, ' asd')
try {
const data = {
name: req.body.name,
description: req.body.description,
processor: req.body.processor,
ram: req.body.ram,
storage: req.body.storage,
imagesColors: req.files,
price: req.body.price,
type: req.body.type,
likes: req.body.likes
}
let product = await productService.create(data)
res.status(201).json(product)
} catch (error) {
res.status(500).json({error: error})
}
})
控制台中请求的结果:
您正在发送 imageColors
作为对象数组(不是文件数组),其中包括图像文件列表和颜色。您还需要在表单数据键的末尾添加方括号,否则将无法正确解析。
您可以在发送前映射 imageColors 对象:
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'}))
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)
})
要使用 FormData API 在前端发送多个文件,您必须一个接一个地附加这些文件。您可以将它们附加到同一个字段,该字段将作为数组到达后端。如果您想随每个文件一起发送附加数据,请将该数据以相同的顺序附加到 不同的 字段。
在你的情况下,它看起来像这样:
for (const imageAndColor of imagesColors) {
data.append('images', imageAndColor.image);
data.append('colors', imageAndColor.color);
}
并在后端将 uploads.array("imagesColors")
更改为 uploads.array("images")
。您的图像将在 req.files
中,颜色将在 req.body.colors
中。保证两个数组的顺序——第一个图像是两个数组中的第一个元素,第二个图像是第二个元素,依此类推。
我正在尝试在我的项目中使用 multer 将图像作为文件上传,但我不知道为什么我在 req.files 上看到的控制台后端中的 onCreate 请求:[]; (空数组)。
这是我创建元素的地方:
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>
);
})}
当我console.log状态imagesColors
它returns:
这里是创建函数,后端有多个函数:
const imageStorage = multer.diskStorage({
destination: (req, file, callback) => {
callback(null, "../../shpJS/frontend/src/styles/images")
},
filename: (req, file, callback) => {
callback(null, file.name)
}
})
const uploads = multer({storage: imageStorage})
router.post('/create', uploads.array("imagesColors"), async (req, res) => {
console.log(req, ' asd')
try {
const data = {
name: req.body.name,
description: req.body.description,
processor: req.body.processor,
ram: req.body.ram,
storage: req.body.storage,
imagesColors: req.files,
price: req.body.price,
type: req.body.type,
likes: req.body.likes
}
let product = await productService.create(data)
res.status(201).json(product)
} catch (error) {
res.status(500).json({error: error})
}
})
控制台中请求的结果:
您正在发送 imageColors
作为对象数组(不是文件数组),其中包括图像文件列表和颜色。您还需要在表单数据键的末尾添加方括号,否则将无法正确解析。
您可以在发送前映射 imageColors 对象:
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'}))
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)
})
要使用 FormData API 在前端发送多个文件,您必须一个接一个地附加这些文件。您可以将它们附加到同一个字段,该字段将作为数组到达后端。如果您想随每个文件一起发送附加数据,请将该数据以相同的顺序附加到 不同的 字段。
在你的情况下,它看起来像这样:
for (const imageAndColor of imagesColors) {
data.append('images', imageAndColor.image);
data.append('colors', imageAndColor.color);
}
并在后端将 uploads.array("imagesColors")
更改为 uploads.array("images")
。您的图像将在 req.files
中,颜色将在 req.body.colors
中。保证两个数组的顺序——第一个图像是两个数组中的第一个元素,第二个图像是第二个元素,依此类推。