You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

React+Node.js+MongoDB项目:上传图片至数据库后无法显示求助

图片上传后无法显示的问题排查与解决

我正在使用React、Node.js、Express和MongoDB做一个小型练习,尝试通过表单将图片上传至数据库并展示。代码本身可以运行,但无法显示图片,求问题原因及解决方法。

后端代码

const multer = require('multer')

const storage = multer.diskStorage({
  destination: function (req, file, cb) {
    cb(null, 'public/uploads/')
  },
  filename: function (req, file, cb) {
    cb(null, file.originalname)
  }
})

const upload = multer({ storage })

app.post('/upload', upload.single('file'), async (req, res) => {
  try {
    const newFile = new File({
      filename: req.file.originalname,
      contentType: req.file.mimetype,
      destination: req.file.destination,
      fileName: req.file.filename,
      path: req.file.path,
      buffer: req.file.buffer,
      fieldname: req.file.fieldname,
      data: req.file.buffer
    })
    // Guardar el documento en la base de datos
    const savedImage = await newFile.save()
    res.json(savedImage)
  } catch (error) {
    console.error(error)
    res.status(500).send('Error al subir el archivo')
  }
})

app.get('/image/:id', async (req, res, next) => {
  const { id } = req.params

  try {
    const file = await File.findById(id)

    if (file) {
      res.set('Content-Type', file.contentType)
      res.send(file.buffer)
    } else {
      res.status(500).send('Error al buscar la imagen')
    }
  } catch (error) {
    next(error)
  }
})

前端代码

function App() {
  const { register, handleSubmit } = useForm();
  const [imagen , setImagen] = useState(null);
  const [loading, setLoading] = useState(false);

  const onSubmit = async (data) => {
    setLoading(true);
    const formData = new FormData();
    formData.append("file", data.file[0]);
    try {
      const response = await axios.post('http://localhost:3001/upload', formData, {
        headers: { 'Content-Type': 'multipart/form-data' },
    });
      const res = await axios.get(`http://localhost:3001/image/${response.data.id}`);
      setImagen(res.data);
    } catch (error) {
      console.error(error);
    } finally {
      setLoading(false);
    }
  };
  
  return (
    <div className="App">
      <form onSubmit={handleSubmit(onSubmit)}>
        <input type="file" {...register("file")} />
        <button type="submit" disabled={loading}>{loading ? "Cargando..." : "Enviar"}</button>
      </form> 
      {imagen && (
        <div>
          <h1>Imagen:</h1>
          <img src={`http://localhost:3001/${imagen.path}`} alt={imagen.fileName} width="500" height="600" />
        </div>
      )
      }
    </div>
  );
}

export default App;

问题原因分析

  1. 前端数据获取逻辑错误:调用/image/:id接口后,你把返回的二进制图片数据赋值给了imagen,但二进制数据里并没有path字段,导致img标签的src路径无效。
  2. 静态文件未托管:后端没有配置静态文件服务,即使path正确,public/uploads目录下的图片也无法通过URL直接访问。
  3. Multer磁盘存储模式下buffer为空:使用diskStorage时,文件会被写入磁盘,不会加载到内存,所以req.file.buffer是undefined,导致存入MongoDB的图片二进制数据为空,/image/:id接口实际返回空内容。

解决方案

方案一:直接通过数据库二进制数据展示图片

这种方式不需要依赖磁盘文件,直接用MongoDB中存储的二进制数据返回图片:

  1. 修改Multer存储模式为内存存储:确保req.file.buffer能获取到图片二进制数据
// 替换原来的diskStorage
const storage = multer.memoryStorage();
const upload = multer({ storage });
  1. 修改前端逻辑:不需要调用/image/:id接口获取数据,直接用该接口URL作为img的src
const onSubmit = async (data) => {
  setLoading(true);
  const formData = new FormData();
  formData.append("file", data.file[0]);
  try {
    const response = await axios.post('http://localhost:3001/upload', formData, {
      headers: { 'Content-Type': 'multipart/form-data' },
    });
    // 保存上传返回的图片文档,用于构造src
    setImagen(response.data);
  } catch (error) {
    console.error(error);
  } finally {
    setLoading(false);
  }
};
  1. 修改img标签的src:
<img src={`http://localhost:3001/image/${imagen._id}`} alt={imagen.fileName} width="500" height="600" />

方案二:通过磁盘文件展示图片(需配置静态托管)

这种方式依赖磁盘存储的文件,需要让Express能访问到public/uploads目录:

  1. 后端配置静态文件托管:
const express = require('express');
const app = express();
// 让/uploads路径映射到public/uploads目录
app.use('/uploads', express.static('public/uploads'));
  1. 后端处理图片路径:存储时去掉path中的public/前缀,确保路径能被静态服务访问
const newFile = new File({
  filename: req.file.originalname,
  contentType: req.file.mimetype,
  destination: req.file.destination,
  fileName: req.file.filename,
  path: req.file.path.replace('public/', ''), // 替换掉public/,得到/uploads/xxx.jpg
  fieldname: req.file.fieldname
  // 不需要存buffer,因为用磁盘文件展示
})
  1. 修改前端逻辑:去掉/image/:id接口的调用,直接用返回的path构造src
const onSubmit = async (data) => {
  setLoading(true);
  const formData = new FormData();
  formData.append("file", data.file[0]);
  try {
    const response = await axios.post('http://localhost:3001/upload', formData, {
      headers: { 'Content-Type': 'multipart/form-data' },
    });
    setImagen(response.data);
  } catch (error) {
    console.error(error);
  } finally {
    setLoading(false);
  }
};
  1. img标签保持原写法即可:
<img src={`http://localhost:3001/${imagen.path}`} alt={imagen.fileName} width="500" height="600" />

内容的提问来源于stack exchange,提问作者David Blanch

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.29 16:45:20