如何使用 Express 和 Multer 上传文件后进行下载?

7
我已经使用 multer 将文件上传到后端的文件系统中。
我的服务器使用node,客户端使用react
我在下载和在客户端的react上显示保存的文件方面遇到了麻烦。
每当我执行res.download(file)时,它只是在客户端抛出一个连接被拒绝的错误。
我的代码如下: UserToUploadMapping.js
const mongoose = require("mongoose");

const UserToUploadMapping = new mongoose.Schema({
  userId: {
      type:String,
      required:true
  },
  file: {
    type: Object,
    required: true,
  },
  date: {
    type: Date,
    default: Date.now,
  },
});

module.exports = mongoose.model("UserToUploadMapping", UserToUploadMapping);

uploadVideo.js

const router = require("express").Router();
const multer = require('multer');
const UserToUploadMapping = require('../models/UserToUploadMapping')

let nameFile = ''
const storage = multer.diskStorage({
    destination:'./Videos',
    filename:(req,file,cb) => {
        console.log(file)
        nameFile = file.originalname + " "+ Date.now()
        cb(null, nameFile)
    }
})

const upload = multer({storage:storage})

router.post('/upload', upload.single('video'), async (req,res,next) => {
    console.log("object")
    const saveMapping = new UserToUploadMapping({
        userId:'123',
        file:req.file,
    })

    await saveMapping.save()

    res.send("Video uploaded")
})

router.get('/download', async(req,res,next) => {
    const x = await UserToUploadMapping.find()
    // res.send(x)
    res.download(x[0].path)
})

module.exports = router;

客户端

const fetchVideo = async () => {
  
    const resp = await axios.get(
      "http://localhost:5000/api/user/video/download"
    );
    console.log(resp)
  };

  return (
    <>
      <NavContainer />
      <div className={classes.Post}>
        <Input
          type="file"
          onChange={(e) => uploadVideos(e.target.files)}
          accept="video/mp4"
        />
        {/* <Button onClick={(e) => submitHandler(e)}>Upload</Button> */}
        <video></video>
      </div>
    </>
  );

错误

这里输入图片描述


1
请在问题中添加错误消息。 - quentino
1
ERR:连接被拒绝。这就是它所说的。 - Phil
1
似乎在 http://localhost:5000 上没有服务器。您如何运行 Express 服务器? - quentino
1
如果您有任何服务器日志,请添加它们,并检查您是否能够成功找到函数中的'x'. - Venkatesh A
1
看起来端点 http://localhost:5000/api/user/video/download 不存在。您是如何在Express中将 router 挂载到应用程序中的? - Maxim Orlov
显示剩余2条评论
5个回答

4

uploadVideo.js文件中存在一些问题:

  • 要从数据中获取path,需要使用x[0].file.path

(根据你在数据库中保存文件的方式)

const saveMapping = new UserToUploadMapping({
        userId:'123',
        file:req.file,
    })
  • 为了避免有关文件uploadVideo.js在何处以及我们运行应用程序的问题,保存系统中的文件时应使用绝对路径。
  • (小问题)您的filename函数将生成像这样的文件名video.mp4 1622180824748。我认为这更好:"video-1622181268053.mp4"(我们有正确的文件扩展名)

您可以参考以下代码:

const router = require("express").Router();
const multer = require('multer');
const UserToUploadMapping = require('../models/UserToUploadMapping')
const path = require('path');
const uploadFolder = path.join(__dirname, "Videos"); // use a variable to hold the value of upload folder

const storage = multer.diskStorage({
    destination: uploadFolder, // use it when upload
    filename: (req, file, cb) => {
        // nameFile = file.originalname + " "+ Date.now() // --> give "video.mp4 1622180824748"
        let [filename, extension] = file.originalname.split('.');
        let nameFile = filename + "-" + Date.now() + "." + extension; // --> give "video-1622181268053.mp4"
        cb(null, nameFile)
    }
})

const upload = multer({ storage: storage })

router.post('/upload', upload.single('video'), async (req, res, next) => {
    const saveMapping = new UserToUploadMapping({
        userId: '123',
        file: req.file,
    })

    await saveMapping.save()

    res.send("Video uploaded")
})

router.get('/download', async (req, res, next) => {
    const video = await UserToUploadMapping.find({});
    res.download(video[0].file.path); // video[0].file.path is the absolute path to the file
})

module.exports = router;

虽然这个答案解决了后端的问题,但客户端仍然不完整,我正在面临问题。请回答客户端的修改以使其正常工作,我会奖励您的赏金。 - Phil

2
你的代码表明你正在处理大文件(视频)。根据我的经验,强烈建议考虑关注点分离,将其作为其他业务逻辑的一部分处理并不推荐。例如,当需要防火墙规则和DDOS保护时,这可能会使事情变得更加复杂。
至少,将上传和下载移动到自己的服务器上,例如“files.yourappnamehere.com”,以便您可以将特定内容与业务逻辑API分开处理。
如果您在公共云中运行,则强烈建议查看重用blob上传/下载功能,让客户直接上传到blob存储并直接从blob存储处理下载,例如在Azure、AWS或GCP中。
这将节省大量处理(非常)大文件的实现细节,并提供“免费”的可扩展性选项,例如在文件上传完成时的事件。

谢谢您的见解。然而,这并不是一个很大的文件,这只是一个学习项目。由于我对处理视频或图像上传还很陌生,所以我想尝试一下。 - Phil

1

看起来你的get处理程序可能有拼写错误...你引用了一个叫做'path'的元素,但是在你的模式中没有声明

router.get('/download', async(req,res,next) => {
    const x = await UserToUploadMapping.find()
    // res.send(x)
    res.download(x[0].path)//<-Path Doesn't seem to be in the schema
})

由于该函数没有try/catch语句,导致可能会出现错误,从而使服务器崩溃,无法使用。

您也可以查看如何使用axios下载文件以获取更详细的信息。


1

您正在运行两个应用程序Frontend和Backend,使用不同的端口(3000、5000),因此浏览器会阻止跨域请求。在Express中,您必须启用CORS以允许来自FrontEnd Url(http://localhost:3000)的请求。


我在问题中提到只有下载路线失败了,其他一切都正常工作,是的,我已经启用了cors - Phil

1

对于下载路线,尝试使用 window.location 函数而不是使用 Axios


2
请提供完整的答案,附上代码。 - Phil
@Phil,你可以使用这个代码在客户端直接启动下载:window.location.assign(ROUTE_FOR_DOWNLOAD);你可以在这里找到更多解决方案:(https://dev59.com/uXNA5IYBdhLWcg3wKai3) - Omer Haqqani

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接