上传图片至AWS S3后,如何将图片链接存入MongoDB?
问题分析与解决方案
你的核心问题有三个:
- 混淆了
req.file和S3返回结果的字段:location是AWS S3上传成功后返回的响应字段,不是multer解析后的req.file自带属性,你从req.file里拿当然拿不到。 - Mongoose Schema里没有定义存储URL的字段:你代码里给
item.imageUrl赋值,但itemSchema里根本没这个字段,就算save也不会存入MongoDB。 - 不必要地将文件buffer存入数据库:你的
image字段是String类型,却试图存Buffer数据,这会导致数据类型不匹配。
另外发现一个潜在bug:s3.js里的AWS配置项拼写错误,accessKeyid应该是accessKeyId(注意大写的I),虽然你说上传正常,但这个拼写错误可能在某些场景下导致认证失败,建议修正。
具体修改步骤
1. 修正S3配置的拼写错误(s3.js)
// s3.js const AWS = require('aws-sdk') const awsConfig = { // 修正拼写:accessKeyid → accessKeyId accessKeyId : process.env.S3_ACCESS_KEY, secretAccessKey : process.env.S3_ACCESS_SECRET, region : process.env.S3_REGION } const S3 = new AWS.S3(awsConfig) const uploadToS3 = (fileData) => { return new Promise ((resolve, reject) =>{ const params = { Bucket : process.env.S3_BUCKET_NAME, Key: `${Date.now().toString()}.jpg`, Body: fileData, // 可选:添加ContentType,让S3返回正确的文件类型头 ContentType: 'image/jpeg' } S3.upload(params, (err, data) =>{ if(err){ console.log(err) reject(err) } console.log(data) resolve(data) }) }) } module.exports = { uploadToS3 }
2. 修改Mongoose Schema,添加存储URL的字段(ItemModel.js)
把原来的image字段改成用来存S3 URL,或者新增imageUrl字段,这里直接修改image字段用途更合理:
const mongoose = require('mongoose') const Schema = mongoose.Schema const itemSchema = new Schema ({ title: { type: String, required: true }, producer: { type: String, required: false }, model: { type: String, required: false }, serialNumber: { type: String, required: false }, yearOfProduction:{ type: Number, required: false }, seller:{ type: String, required: false }, purchaseDate: { type: Date, default: Date.now }, warrantyDate: { type: Date, required: false }, atEmployee: { type: mongoose.Schema.Types.ObjectId, required: false, ref:'Worker', }, // 改为存储S3图片URL image: { type: String, required: false, } }, { timestamps: true }) module.exports = mongoose.model('Item', itemSchema)
3. 调整控制器逻辑,正确获取并存储S3 URL(itemController.js)
不需要先存buffer到数据库,直接上传S3后把URL存入:
const Item = require('../models/itemModel') const Worker = require('../models/workerModel') const mongoose = require('mongoose') const multer = require('multer') const { uploadToS3 } = require('../s3') const multerConfig = { limits: 1024 * 1024 * 5, fileFilter: function (req, file, done) { const allowedTypes = ["image/jpg", "image/png", "image/jpeg"] if (allowedTypes.includes(file.mimetype)) { done(null, true) } else { done("Niewłaściwy plik, użyj .jpg .jpeg .png", false) } } } const upload = multer(multerConfig) const createItem = async (req, res) => { upload.single("image")(req, res, async () => { const { title, model, producer, serialNumber, yearOfProduction, atEmployee, seller, warrantyDate, purchaseDate, image: existingImage, } = req.body if (!title){ return res.status(400).json({error:'Błąd! Wymagane jest podanie chociaż nazwy narzędzia.'}) } try { let imageUrl = existingImage // 如果有上传文件,先传到S3拿到URL if (req.file) { const result = await uploadToS3(req.file.buffer) imageUrl = result.location } // 直接创建带URL的Item,不需要先存再更新 const item = await Item.create({ title, model, producer, serialNumber, yearOfProduction, atEmployee, seller, warrantyDate, purchaseDate, image: imageUrl, }) res.status(200).json(item) } catch(error) { res.status(400).json({error: error.message}) } }) }
关键说明
req.file是multer解析上传文件后生成的对象,包含buffer、originalname、mimetype等属性,但不包含S3的location,这个字段只有当你调用S3.upload成功后,从返回的data对象里获取。- 不要把文件buffer存入MongoDB,一是浪费数据库空间,二是String类型字段存Buffer会导致数据异常,直接存S3的URL是最优方案。
- 如果你需要兼容已有数据(比如之前存了其他图片链接),可以保留
existingImage的逻辑,优先用新上传的S3 URL,没有上传时用传入的image值。
内容的提问来源于stack exchange,提问作者Tomasz
相关产品推荐
相关产品推荐

