node——分段上传大文件

本文介绍如何使用Node.js在后端实现大文件的分段上传功能,结合前端页面,确保上传过程的稳定性和效率。通过分块处理,优化了上传大文件时的内存管理和网络传输效率。
摘要由CSDN通过智能技术生成

前端页面

<body>
    <input type="file" id=file>
    <!-- 进度条 -->
    <div id='pro'></div>

    <script>
        let [file, pro] = [document.getElementById('file'), document.getElementById('pro')]

        file.onchange = () => {
            upload()
        }

        function upload(start = 0, index = 0, chunkSize = 1024 * 1024) {
            let f = file.files[0]

            if (start >= f.size) return
            let end = start + chunkSize >= f.size ? f.size : start + chunkSize

            // blob 和 file 相互转换
            let aimfile = new File(
                [f.slice(start, end)],
                `${f.name.split('.')[0]}.${index}.${f.name.split('.')[1]}`,
                { type: f.type, lastModified: Date.now() }
            )

            let form = new FormData()
            form.append('file', aimfile)

            ajax(form, (flag) => {
                if (flag) {
                    pro.style.width = end / f.size * 200 + 'px'
                    upload(end, ++index)
                }
            })
        }

        function ajax(file, cb, method = 'POST', url = 'http://localhost:5000/upload') {
            let xhr = new XMLHttpRequest()
            xhr.open(method, url, true)
            xhr.send(file)
            xhr.onreadystatechange = () => {
                if (xhr.readyState === 4 && xhr.status === 200) {
                    cb(xhr.responseText)
                }
            }
        }
    </script>
</body>

node 后端

const express = require('express')
const path = require('path')
const multer = require('multer')
const fs = require('fs')

let timer = null
let storage = multer.diskStorage({
    destination: function (req, file, cb) {
        // 新增目录存放文件
        const chunksPath = path.join('public/upload', file.originalname.split('.')[0]);
        if (!fs.existsSync(chunksPath)) {
            fs.mkdirSync(chunksPath, { recursive: true }); // 新建文件夹
        }

        // 配置文件保存路径
        cb(null, path.join('public/upload', file.originalname.split('.')[0]))

        // 合并文件并删除目录
        clearTimeout(timer)
        timer = setTimeout(async () => {
            // 合并文件(获取子文件名称)
            await fs.readdir(chunksPath, (err, files) => {
                if (err) throw err
                files.map((res) => {
                    // 同步合并文件
                    fs.appendFileSync(
                        `public/upload/${file.originalname.split('.')[0]}.${file.originalname.split('.')[2]}`,
                        fs.readFileSync(path.join(chunksPath, res)), // 读取文件
                        (err) => { if (err) throw err }
                    )
                });
            })

            // 删除文件
            const delFile = () => {
                fs.readdir(chunksPath, (err, files) => {
                    if (err) throw err
                    if (!files.length) {
                        delFile()
                    } else {
                        files.map(res => {
                            fs.unlinkSync(path.join(chunksPath, res), (e) => { if (err) throw err });
                        })
                    }
                })
            }
            await delFile()

            // 删除文件夹
            const del = () => {
                // 判断子文件是否为空
                fs.readdir(chunksPath, (err, files) => {
                    if (files.length != 0) {
                        del()
                    } else {
                        // 为空则删除文件夹
                        fs.rmdir(chunksPath, (err) => { if (err) throw err })
                    }
                })
            }
            await del()
        }, 500)
    },
    filename: function (req, file, cb) {
        const split = file.originalname.split('.')
        cb(null, `${split[0]}-${split[1]}`) // 文件名
    }
});

let upload = multer({ storage: storage });

let app = express()

// 解决跨域
app.all('*', (req, res, next) => {
    res.header('Access-Control-Allow-Credentials', 'true')
    res.header('Access-Control-Allow-Origin', req.headers.origin)
    res.header('Access-Control-Allow-Methods', 'PUT, POST, GET, DELETE, OPTIONS')
    res.header('Access-Control-Allow-Headers', 'Content-Type, Content-Length, Authorization, Token, Accept, X-Requested-With')
    next()
})

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

app.use('/upload', upload.single('file'), function (req, res, next) {
    if (req.file) res.send(true)
})

app.listen(5000, () => console.log('Example app listening on port 5000!'))
  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值