部署到Heroku后,Node.js+Multer上传图片无法正常显示求助
问题描述
我用React、Node.js、MongoDB开发了一个购物车项目,本地运行时通过Multer库将图片上传至本地文件夹。现在把Node.js部署到Heroku,MongoDB切换为云端服务后,用户及产品信息能正常存储,但图片无法正常加载。推测是云端环境无法使用本地文件夹,导致产品详情中缺失图片。
原有相关代码
config.js
const cors = require("cors"); const express = require("express"); const multer = require("multer"); const setServerConfiguration = (app) => { app.use(express.urlencoded({ extended: true })); app.use('/public',express.static("public")); app.use(express.json()); app.use(cors()); } const storage = multer.diskStorage({ destination: function (req, file, cb) { cb(null, "./public/images/"); }, filename: function (req, file, cb) { cb(null, file.originalname); }, }); const upload = multer({ storage: storage }); module.exports = {setServerConfiguration, upload}
路由文件
const router = require("express").Router(); const { upload } = require("../config"); const ProductModel = require("../models/Product"); const UserModel = require("../models/User"); router.get("/", async (req, res) => { await UserModel.find({}, (err, obj) => { ProductModel.find({}, (err, obj2) => { const temp = [obj, obj2]; res.json(temp); }); }).clone(); }); router.post("/add-product", upload.single("product"), async (req, res) => { let img = req.file.filename; const body = req.body.product; await ProductModel.create({ name: body[0], price: body[1], category: body[2], picture: img, }); res.json("ok"); }); router.delete("/admin", async (req, res) => { let body = req.body.product; res.send(body); await ProductModel.deleteOne({ name: body }, (err, obj) => {}); res.json("ok"); }); router.post("/admin", async (req, res) => { let price = req.body.price; let productName = req.body.productName; const filter = { name: productName }; const update = { price: price }; const opts = { new: true }; await ProductModel.findOneAndUpdate(filter, update, opts); res.json("ok"); }); module.exports = router;
ProductModel.js
const mongoose = require("mongoose"); const ProductSchema = new mongoose.Schema({ id : false, name: String, price: Number, category: String, picture: String, }); const ProductModel = mongoose.model("products", ProductSchema); module.exports = ProductModel;
解决方案
Heroku的文件系统是临时可销毁的,服务器重启或重新部署后,本地存储的文件会全部丢失,因此不能用本地文件夹存储图片。推荐使用云存储服务托管图片,以下以Cloudinary为例给出修改步骤:
步骤1:配置Cloudinary存储
- 安装依赖:
npm install cloudinary multer-storage-cloudinary
- 创建Cloudinary配置文件(如
cloudinaryConfig.js):
const cloudinary = require('cloudinary').v2; const { CloudinaryStorage } = require('multer-storage-cloudinary'); cloudinary.config({ cloud_name: '你的Cloudinary云名称', // 从Cloudinary控制台获取 api_key: '你的API密钥', api_secret: '你的API密钥密码' }); const storage = new CloudinaryStorage({ cloudinary: cloudinary, params: { folder: 'product-images', // 存储图片的云端文件夹名 allowed_formats: ['jpg', 'png', 'jpeg'], filename: (req, file, cb) => { cb(null, `${Date.now()}-${file.originalname}`); // 自定义文件名避免重复 } } }); module.exports = { storage, cloudinary };
步骤2:修改Multer配置
替换原本地存储配置,更新config.js:
const cors = require("cors"); const express = require("express"); const multer = require("multer"); const { storage } = require('./cloudinaryConfig'); const setServerConfiguration = (app) => { app.use(express.urlencoded({ extended: true })); // 不再需要托管本地public文件夹 app.use(express.json()); app.use(cors()); } const upload = multer({ storage: storage }); module.exports = {setServerConfiguration, upload}
步骤3:更新产品上传接口
将数据库中存储的文件名替换为Cloudinary返回的图片URL,修改路由文件中的/add-product接口:
router.post("/add-product", upload.single("product"), async (req, res) => { const imgUrl = req.file.path; // 获取Cloudinary返回的图片完整URL const body = req.body.product; await ProductModel.create({ name: body[0], price: body[1], category: body[2], picture: imgUrl, // 存储完整图片URL }); res.json("ok"); });
步骤4:前端调整图片加载逻辑
前端直接使用数据库中存储的完整Cloudinary URL加载图片即可:
<img src={product.picture} alt={product.name} />
额外注意事项
- 不要在代码中硬编码Cloudinary密钥,建议用
dotenv管理环境变量,并在Heroku控制台配置对应环境变量 - 若使用AWS S3、Firebase Storage等其他云存储,逻辑类似:替换Multer的存储引擎,将图片远程URL存入数据库
内容的提问来源于stack exchange,提问作者Lior98
相关产品推荐
相关产品推荐

