【Node.js】大文件上传

概述

大文件上传通常采用分片上传。如果因为某些原因上传突然中断,解决问题之后可以接着之前的分片上传,而不需要从头开始上传,也就是断点续传。此外还可以利用多个网络连接并行上传多个分片,提高上传速度。

注:前端不能使用 live-server 去启动, live-server 启动会在上传文件时自动刷新页面,阻止默认事件。所以使用 npm i http-server 去启动 http-server -p 9999这里用了 9999 端口。

<html>
<head>
    <meta charset="UTF-8">
    <meta name="viewport"
          content="width=device-width, user-scalable=no, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0">
    <meta http-equiv="X-UA-Compatible" content="ie=edge">
    <title>Document</title>
</head>
<body>
    <input id="file" type="file">
    <script>
        const file = document.querySelector('#file')
        file.addEventListener('change', e => {
            let file = e.target.files[0]  // file 对象
            console.log(file)
        })
    </script>
</body>
</html>

此时上传一个 map4 文件。

image.png

分片上传。

image.png

完整代码

前端

<html>
<head>
    <meta charset="UTF-8">
    <meta name="viewport"
          content="width=device-width, user-scalable=no, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0">
    <meta http-equiv="X-UA-Compatible" content="ie=edge">
    <title>Document</title>
</head>
<body>
<input id="file" type="file">
<script>
    const file = document.querySelector('#file')
    // 2 MB 的文件 我这里 以 1MB 为一个切片
    const chunksFn = (file, size = 1024 * 1024) => {
        const chunks = []
        for (let i = 0; i < file.size; i += size) {
            chunks.push(file.slice(i, i + size))
        }
        return chunks
    }
    const uploadFiles = (chunks) => {
        // 批量上传 Promise.all
        const list = []
        for (let i = 0; i < chunks.length; i++) {
            const formData = new FormData()
            formData.append('index', i)  // 标识
            formData.append('filename', 'videoTest')
            formData.append('file', chunks[i])  // 必须写最后,因为multer读到file,之后的index,filename等(自定义的)就不会处理了
            list.push(fetch('http://localhost:3000/upload', {
                method: 'POST',
                body: formData
            }))
        }
        // 等待所有请求发送完成,通知后端合并切片
        Promise.all(list).then(res => {
            fetch('http://localhost:3000/merge', {
                method: 'POST',
                headers: {
                    'Content-Type': 'application/json'
                },
                body: JSON.stringify({
                    filename: 'videoTest'
                })
            })
            console.log('上传成功')
        })
    }
    file.addEventListener('change', e => {
        // files 对象 底层继承 blob 可以调用 slice 方法进行切割
        let file = e.target.files[0]
        const chunks = chunksFn(file)
        uploadFiles(chunks)
    })
</script>
</body>
</html>

后端

import multer from 'multer'
import express from 'express'
import cors from 'cors'
import fs from 'fs'
import path from "path";

// 初始化 multer
const storage = multer.diskStorage({
    destination: function (req, res, cb) {
        cb(null, 'upload/') // 分片存储的目录
    },
    filename(req, res, cb) {
        cb(null, `${req.body.index}-${req.body.filename}`)
    }
})
const upload = multer({storage})
const app = express()
app.use(cors())
app.use(express.json())
app.post('/upload', upload.single('file'), (req, res) => {
    res.send('ok')
})
app.post('/merge', (req, res) => {
    // 读取目录下面的所有切片
    const uploadDir = path.join(process.cwd(), 'upload')
    const uploadArr = fs.readdirSync(uploadDir)
    // 排序,再进行拼接
    uploadArr.sort((a, b) => a.split('-')[0] - b.split('-')[0])
    const videoDir = path.join(process.cwd(), 'video', `${req.body.filename}.mp4`)
    uploadArr.forEach(item => {
        fs.appendFileSync(videoDir, fs.readFileSync(path.join(uploadDir, item)))
        // 分片合并后就可以清除了
        fs.unlinkSync(path.join(uploadDir, item))
    })
    res.send('ok')
})
app.listen(3000, (req, res) => {
    console.log('3000端口已启动')
})

相关推荐

  1. vue3 + Element + nodejs 文件、断点续

    2024-03-31 20:00:07       48 阅读
  2. 文件分块

    2024-03-31 20:00:07       30 阅读
  3. 文件实现

    2024-03-31 20:00:07       28 阅读
  4. 文件的分片和断点

    2024-03-31 20:00:07       40 阅读

最近更新

  1. docker php8.1+nginx base 镜像 dockerfile 配置

    2024-03-31 20:00:07       94 阅读
  2. Could not load dynamic library ‘cudart64_100.dll‘

    2024-03-31 20:00:07       100 阅读
  3. 在Django里面运行非项目文件

    2024-03-31 20:00:07       82 阅读
  4. Python语言-面向对象

    2024-03-31 20:00:07       91 阅读

热门阅读

  1. 前端广名词知识补充

    2024-03-31 20:00:07       33 阅读
  2. 安卓开发Gson插件的使用

    2024-03-31 20:00:07       35 阅读
  3. 新手如何学好linux的建议

    2024-03-31 20:00:07       40 阅读
  4. H12-821_182

    2024-03-31 20:00:07       38 阅读
  5. 小红书Android实习面经

    2024-03-31 20:00:07       41 阅读
  6. 【质量管理】

    2024-03-31 20:00:07       37 阅读
  7. PTA:7-109 公园门票-zzuli

    2024-03-31 20:00:07       38 阅读
  8. Rust语言教程

    2024-03-31 20:00:07       37 阅读
  9. 代码随想录算法训练营 Day39 动态规划2

    2024-03-31 20:00:07       35 阅读
  10. 作业练习(python)

    2024-03-31 20:00:07       34 阅读
  11. 使用WebRTC实现简单直播

    2024-03-31 20:00:07       39 阅读