Nodejs 第五十八章(大文件上传)

在现代网站中,越来越多的个性化图片,视频,去展示,因此我们的网站一般都会支持文件上传。

文件上传的方案

  1. 大文件上传:将大文件切分成较小的片段(通常称为分片或块),然后逐个上传这些分片。这种方法可以提高上传的稳定性,因为如果某个分片上传失败,只需要重新上传该分片而不需要重新上传整个文件。同时,分片上传还可以利用多个网络连接并行上传多个分片,提高上传速度。
  2. 断点续传:在上传过程中,如果网络中断或上传被中止,断点续传技术可以记录已成功上传的分片信息,以便在恢复上传时继续上传未完成的部分,而不需要重新上传整个文件。这种技术可以大大减少上传失败的影响,并节省时间和带宽。

前端实现

html 复制代码
<input id="file" type="file"> <!--用来上传文件-->

监听change事件获取文件 file,实现一个方法 chunkFun 用来切片

js 复制代码
const file = document.getElementById('file')
file.addEventListener('change', (event) => {
    const file = event.target.files[0] //获取文件信息
    const chunks = chunkFun(file)
    uploadFile(chunks)
})

文件切片 file 接受文件对象,注意file的底层是继承于blob的因此他可以调用blob的方法,slice进行切片,size就是每个切片的大小,我这里用了4MB 实际可以根据项目情况来

js 复制代码
const chunkFun = (file, size = 1024 * 1024 * 4) => {
    const chunks = []
    for (let i = 0; i < file.size; i += size) {
        chunks.push(file.slice(i, i + size))
    }
    return chunks
}

循环调用接口上传,并且存储一些信息,当前分片的索引,注意file必须写在最后一个,因为nodejs端的multer 会按照顺序去读的,不然读不到参数, 最后通过promise.all 并发发送请求,等待所有请求发送完成,通知后端合并切片。

js 复制代码
const uploadFile = (chunks) => {
        const List = []
        for (let i = 0; i < chunks.length; i++) {
        const formData = new FormData()
        formData.append('index', i)
        formData.append('total', chunks.length)
        formData.append('fileName', 'xiezhen')
        formData.append('file', chunks[i])
        List.push(fetch('http://127.0.0.1:3000/up', {
            method: 'POST',
            body: formData
        }))
    }
    Promise.all(List).then(res => {
        fetch('http://127.0.0.1:3000/merge',{
            method: 'POST',
            headers:{
               'Content-Type': 'application/json'
            },
            body:JSON.stringify({
                fileName: 'xiaoManXieZhen',
            })
        }).then(res => {
            console.log(res)
        })
    })
}

nodejs端实现

安装依赖

  1. express 帮我们启动服务,并且提供接口
  2. multer 读取文件,存储
  3. cors 解决跨域

引入模块

js 复制代码
import express from 'express'
import multer from 'multer'
import cors from 'cors'
import fs from 'node:fs'
import path from 'node:path'

提供两个接口

  1. up 用来存储切片
  2. merge 合并切片

初始化 multer.diskStorage

  • destination 存储的目录
  • filename 存储的文件名(我是通过index-文件名存储的你也可以改)

合并逻辑

读取upload目录下面的所有文件 也就是所有的切片

txt 复制代码
0-xiezhen
1-xiezhen
2-xiezhen
3-xiezhen

读取之后返回的是一个数组,但是读取的时候会乱序,所以从小到大排个序,用了sort,排完序之后,读取每个切片的内容,通过 fs.appendFileSync 合并至一个文件,最后删除合并过的切片 完成。respect

js 复制代码
const storage = multer.diskStorage({
    destination: (req, file, cb) => {
        cb(null, 'uploads/')
    },
    filename: (req, file, 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('/up', upload.single('file'), (req, res) => {
    res.send('ok')
})

app.post('/merge', async (req, res) => {
    const uploadPath = './uploads'
    let files = fs.readdirSync(path.join(process.cwd(), uploadPath))
    files = files.sort((a, b) => a.split('-')[0] - b.split('-')[0])
    const writePath = path.join(process.cwd(), `video`, `${req.body.fileName}.mp4`)
    files.forEach((item) => {
        fs.appendFileSync(writePath, fs.readFileSync(path.join(process.cwd(), uploadPath, item)))
        fs.unlinkSync(path.join(process.cwd(), uploadPath, item))
    })

    res.send('ok')
})

app.listen(3000, () => {
    console.log('Server is running on port 3000')
})
相关推荐
我要洋人死11 分钟前
导航栏及下拉菜单的实现
前端·css·css3
科技探秘人22 分钟前
Chrome与火狐哪个浏览器的隐私追踪功能更好
前端·chrome
科技探秘人23 分钟前
Chrome与傲游浏览器性能与功能的深度对比
前端·chrome
JerryXZR28 分钟前
前端开发中ES6的技术细节二
前端·javascript·es6
七星静香30 分钟前
laravel chunkById 分块查询 使用时的问题
java·前端·laravel
q24985969333 分钟前
前端预览word、excel、ppt
前端·word·excel
小华同学ai38 分钟前
wflow-web:开源啦 ,高仿钉钉、飞书、企业微信的审批流程设计器,轻松打造属于你的工作流设计器
前端·钉钉·飞书
Gavin_9151 小时前
【JavaScript】模块化开发
前端·javascript·vue.js
懒大王爱吃狼2 小时前
Python教程:python枚举类定义和使用
开发语言·前端·javascript·python·python基础·python编程·python书籍
逐·風6 小时前
unity关于自定义渲染、内存管理、性能调优、复杂物理模拟、并行计算以及插件开发
前端·unity·c#