node——分段上传大文件

前端页面

<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!'))

你可能感兴趣的:(node)