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

基于Multer+Express+Node.js实现前端查看最近上传文件的问题求助

Express+Multer实现文件上传后前端查看已上传文件

问题背景

已通过Multer、Express.js和Node.js完成文件上传功能,想要在前端添加查看按钮以展示最近上传的文件,但尝试以下代码后出现错误:

let fname = multer.diskStorage({
  filename: (req, file, cb) => {
    cb(file.originalname);
  },
});

app.get("/", (req, res) => {
  res.render("uploads");
  res.render(fname.toString("utf8"));
});

报错信息:

Error: Failed to lookup view "[object Object]" in views directory "D:\OODU\uploadImagesNodejs\views"

原代码文件

index.js

const express = require("express");
const app = express();
const path = require("path"); //Importing required libraries
const multer = require("multer");

const storage = multer.diskStorage({
  destination: (req, file, cb) => {
    cb(null, "images"); //creating a storage destination for the uploaded files
  },

  filename: (req, file, cb) => {
    console.log(file);
    cb(
      null,
      file.originalname.replace(/\.[^/.]/, "") +
        "_" +
        Date.now() +
        path.extname(file.originalname) //Adding timestamp to the file name to make it as an unique ID
    );
  },
});

const maxSize = 2 * 1024 * 1024; //Limiting the file size within 5MB to upload

let upload = multer({
  storage: storage, //File conditions for uploading (i.e: size, type)
  limits: {
    fileSize: maxSize,
  },
  fileFilter: function (req, file, cb) {
    let filetypes = /jpeg|jpg|png/;
    let mimetype = filetypes.test(file.mimetype);
    let extname = filetypes.test(path.extname(file.originalname).toLowerCase());

    if (mimetype && extname) {
      return cb(null, true);
    }

    cb(
      "Error: File upload only supports the following filetypes: " + filetypes
    );
  },
}).single("image");

app.set("views", path.join(__dirname, "views"));
app.set("view engine", "ejs");

let fname = multer.diskStorage({
  filename: (req, file, cb) => {
    cb(file.originalname);
  },
});

app.get("/", (req, res) => {
  res.render("uploads");
  res.render(fname.toString("utf8"));
});

app.post("/uploads", (req, res, next) => {
  upload(req, res, function (err) {
    if (err) {
      if (err instanceof multer.MulterError && err.code == "LIMIT_FILE_SIZE") {
        return res.send("file exceeds the allowed limit");
      }
      res.send(err);
    } else {
      res.send("uploaded successfully");
    }
  });
});

app.listen(3001);
console.log("3001 is the port");

uploads.ejs

<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <meta http-equiv="X-UA-Compatible" content="IE=edge" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>Document</title>
  </head>
  <body style="justify-content: center; text-align: center">
    <form action="/uploads" method="post" enctype="multipart/form-data">
      <div><b style="color: blue"> File size should be within 2mb</b></div>
      <input type="file" name="image" />
      <input type="submit" value="submit" />
      <a href="/images" class="btn"> view uploads </a>
    </form>
  </body>
</html>

错误分析

  1. fname是Multer的磁盘存储配置对象,调用toString()只会返回[object Object],而res.render需要传入视图文件名,因此触发视图找不到的错误。
  2. 单个HTTP请求只能调用一次res.render,第一次调用已发送响应,第二次调用无效。
  3. 未将上传文件目录设为静态资源,浏览器无法直接访问文件;同时/images路由未做处理,点击查看按钮无法加载内容。

解决方案

1. 配置静态资源托管

将存放上传文件的images目录设置为静态资源,让浏览器可以直接访问其中的文件:

app.use('/images', express.static(path.join(__dirname, 'images')));

2. 修改后端路由,传递文件列表给前端

读取images目录下的文件,按修改时间倒序排序(最近上传的在前),将文件列表传递给EJS视图:

const fs = require('fs'); // 新增fs模块引入

app.get("/", (req, res) => {
  const imagesDir = path.join(__dirname, 'images');
  fs.readdir(imagesDir, (err, files) => {
    if (err) {
      return res.send('读取文件列表失败');
    }
    // 过滤图片文件并按修改时间排序
    const imageFiles = files.filter(file => /\.(jpeg|jpg|png)$/i.test(file));
    const sortedFiles = imageFiles.map(file => ({
      name: file,
      url: `/images/${file}`
    })).sort((a, b) => {
      const statA = fs.statSync(path.join(imagesDir, a.name));
      const statB = fs.statSync(path.join(imagesDir, b.name));
      return statB.mtime.getTime() - statA.mtime.getTime();
    });
    // 将文件列表传给视图
    res.render("uploads", { files: sortedFiles });
  });
});

3. 修改前端视图,展示文件列表

更新uploads.ejs,添加文件列表展示区域,点击按钮可切换显示/隐藏:

<!DOCTYPE html>
<html lang="zh-CN">
<head>
  <meta charset="UTF-8" />
  <meta http-equiv="X-UA-Compatible" content="IE=edge" />
  <meta name="viewport" content="width=device-width, initial-scale=1.0" />
  <title>文件上传与查看</title>
  <style>
    .file-item { margin: 15px 0; }
    .file-item img { max-width: 200px; max-height: 200px; border-radius: 4px; }
    .btn { padding: 6px 12px; background: #007bff; color: white; text-decoration: none; border-radius: 4px; }
  </style>
</head>
<body style="max-width: 800px; margin: 20px auto; text-align: center">
  <form action="/uploads" method="post" enctype="multipart/form-data">
    <div><b style="color: blue"> 文件大小不能超过2MB</b></div>
    <input type="file" name="image" accept="image/jpeg,image/png,image/jpg" style="margin: 10px 0" />
    <input type="submit" value="上传" class="btn" />
    <button type="button" onclick="toggleFileList()" class="btn" style="margin-left: 10px">查看上传文件</button>
  </form>

  <!-- 文件列表区域 -->
  <div id="fileList" style="display: none; margin-top: 30px;">
    <h3>最近上传的文件</h3>
    <% if (files && files.length > 0) { %>
      <div style="display: flex; flex-wrap: wrap; justify-content: center; gap: 20px;">
        <% files.forEach(file => { %>
          <div class="file-item">
            <p><%= file.name %></p>
            <img src="<%= file.url %>" alt="<%= file.name %>" />
          </div>
        <% }) %>
      </div>
    <% } else { %>
      <p>暂无上传文件</p>
    <% } %>
  </div>

  <script>
    function toggleFileList() {
      const list = document.getElementById('fileList');
      list.style.display = list.style.display === 'none' ? 'block' : 'none';
    }
  </script>
</body>
</html>

4. 修正完整后端代码

删除无用的fname配置,优化上传成功后的跳转逻辑:

const express = require("express");
const app = express();
const path = require("path");
const multer = require("multer");
const fs = require('fs');

const storage = multer.diskStorage({
  destination: (req, file, cb) => {
    cb(null, "images");
  },
  filename: (req, file, cb) => {
    // 修正正则,正确去除文件名中的扩展名
    cb(
      null,
      file.originalname.replace(/\.[^/.]+$/, "") +
        "_" +
        Date.now() +
        path.extname(file.originalname)
    );
  },
});

const maxSize = 2 * 1024 * 1024;

let upload = multer({
  storage: storage,
  limits: {
    fileSize: maxSize,
  },
  fileFilter: function (req, file, cb) {
    let filetypes = /jpeg|jpg|png/;
    let mimetype = filetypes.test(file.mimetype);
    let extname = filetypes.test(path.extname(file.originalname).toLowerCase());

    if (mimetype && extname) {
      return cb(null, true);
    }

    cb("错误:仅支持以下格式的文件上传:" + filetypes);
  },
}).single("image");

app.set("views", path.join(__dirname, "views"));
app.set("view engine", "ejs");

// 静态资源托管
app.use('/images', express.static(path.join(__dirname, 'images')));

app.get("/", (req, res) => {
  const imagesDir = path.join(__dirname, 'images');
  fs.readdir(imagesDir, (err, files) => {
    if (err) {
      return res.send('读取文件列表失败');
    }
    const imageFiles = files.filter(file => /\.(jpeg|jpg|png)$/i.test(file));
    const sortedFiles = imageFiles.map(file => ({
      name: file,
      url: `/images/${file}`
    })).sort((a, b) => {
      const statA = fs.statSync(path.join(imagesDir, a.name));
      const statB = fs.statSync(path.join(imagesDir, b.name));
      return statB.mtime.getTime() - statA.mtime.getTime();
    });
    res.render("uploads", { files: sortedFiles });
  });
});

app.post("/uploads", (req, res, next) => {
  upload(req, res, function (err) {
    if (err) {
      if (err instanceof multer.MulterError && err.code == "LIMIT_FILE_SIZE") {
        return res.send("文件大小超过限制(最大2MB)");
      }
      res.send(err);
    } else {
      // 上传成功后重定向回首页,刷新文件列表
      res.redirect('/');
    }
  });
});

app.listen(3001);
console.log("服务器运行在端口3001");

内容的提问来源于stack exchange,提问作者Arrow-02

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.29 14:45:08