Nodejs处理压缩文件

2,243 阅读2分钟

当我们在使用Node搭建服务端的时候,我们可能会碰到需要接收 压缩文件 的情况,例如:接收前端提交的压缩包、解压等,我们这里演示一下如何 接收解压

前端

html

<!-- pages/index.html -->

<p id="desc">点击或拖拽文件到网页内即可上传</p>

css

/* pages/index.html */

* {
    margin: 0;
    padding: 0;
    list-style: none;
    border: none;
    box-sizing: border-box;
}

html,
body {
    width: 100vw;
    height: 100vh;
}

#desc {
    position: fixed;
    top: 50%;
    left: 50%;
    letter-spacing: 1em;
    font-size: 1.25rem;
    color: #7c7c7c;
    transform: translate(-50%, -50%);
    z-index: -9999;
}

js

// pages/index.html

(() => {

    let body = document.body

    body.addEventListener('dragenter', e => e.preventDefault(), false)

    body.addEventListener('dragleave', e => e.preventDefault(), false)

    body.addEventListener('dragover', e => e.preventDefault(), false)
	
    // 拖拽上传
    body.addEventListener('drop', e => {
        e.preventDefault()
        let fileList = e.dataTransfer.files
        beforeUpload(fileList)
    }, false)

    // 点击上传
    body.addEventListener('click', () => {
        let fileInput = document.createElement('input')
        fileInput.type = 'file'
        fileInput.click()
        fileInput.onchange = function () {
            beforeUpload(this.files)
        }
    }, false)

	// 处理要上传的文件
    function beforeUpload(fileList) {
        // 检测是否是拖拽文件到页面的操作
        if (fileList.length === 0) {
            alert('未选择要上传的文件')
            return
        }
        // 检测文件是不是zip压缩包
        if (fileList[0].type.indexOf("application/zip") === -1) {
            alert('只能上传zip格式的压缩包')
            return
        }
		
        let formData = new FormData()
        formData.append('file', fileList[0])

        request(formData)
    }

	// 发送请求
    function request(formData) {
        let xhr = new XMLHttpRequest()
        xhr.open('POST', '/api/upload')
        xhr.send(formData)
        xhr.onload = function () {
            let { response } = this
            response = JSON.parse(response)
            if (response.code === 200 && response.state === true) {
                alert(`上传成功`)
            } else {
                alert(`上传失败,${response.message}`)
            }
        }
    }

})()

后台

后台采用express搭建,执行以下命令进行依赖安装:

yarn add express body-parser multer compressing

npm i express body-parser multer compressing -S

我们的目录结构如下:

node_modules
cache
pages
  - index.html
service
  - index.js
utils
  - index.js
main.js
package.json

入口文件

// main.js

const express = require('express')
const bodyParser = require('body-parser')
const router = require('./service/index')
const path = require('path')

const app = express()

app.use(bodyParser.urlencoded({ extended: false }))
app.use(bodyParser.json())

app.use(express.static(path.resolve(__dirname, 'pages')))

app.use(router)

app.listen(8080, () => {
    console.log(`服务器运行于 http://127.0.0.1:8080`)
})

自定义工具函数

// utils/index.js

const fs = require('fs')
const compressing = require('compressing');

// 写入文件
exports.writeFile = (filepath, data, encode) => {
    return new Promise((resolve, reject) => {
        fs.writeFile(filepath, data, encode, (err) => {
            if (err) reject(err)
            resolve()
        })
    })
}


// 读取文件
exports.readFile = (filepath, encode) => {
    return new Promise((resolve, reject) => {
        fs.readFile(filepath, encode, (err, data) => {
            if (err) reject(err)
            resolve(data)
        })
    })
}


// 解压文件
exports.extract = (filepath, outputPath) => {
	// 
    return compressing.zip.uncompress(filepath, outputPath, {
        // 设置中文解码,重要!!!
        zipFileNameEncoding: 'utf8'
    })

}


// 删除文件和文件夹
exports.delDir = (filepath) => {

    if (fs.existsSync(filepath)) {
        const files = fs.readdirSync(filepath)
        files.forEach((file) => {
            const nextFilePath = `${filepath}/${file}`
            const states = fs.statSync(nextFilePath)
            if (states.isDirectory()) {
                this.delDir(nextFilePath)
            } else {
                fs.unlinkSync(nextFilePath)
            }
        })
        fs.rmdirSync(filepath)
    }
}

路由文件

// router/index.js

const router = require('express').Router()
const fs = require('fs')
const multer = require('multer')
const path = require('path')
const { writeFile, extract, delDir } = require('../utils/index')
// 压缩文件存放的位置
const filePath = path.resolve(__dirname, '../', 'cache')

router.post('/api/upload', multer().single('file'), async (req, res) => {
    // multer 会自动将文件数据绑定到req.file中
    let file = req.file

    if (file.mimetype !== 'application/zip') {
        res.send({ code: 400, state: false, message: '不是zip文件', data: 0 })
    }

    try {
		// 删除旧目录
        delDir(filePath)
		// 重建压缩文件存放的目录
        fs.mkdirSync(filePath)
		// 得到压缩包的文件名
        let filename = path.join(filePath, file.originalname)
        // 保存压缩包文件,file.buffer存放文件的二进制数据
        await writeFile(filename, file.buffer, 'buffer')
        // 解压压缩包
        await extract(filename, filePath)
        // 解压完成后,删除压缩包
        fs.unlinkSync(filename)

        res.send({ code: 200, state: true, message: 'ok' })
    } catch (error) {
        console.log(error)
        res.send({ code: 400, state: false, message: 'zip文件解压失败', data: 0 })
    }

})

module.exports = router

其他

我们解压使用的是 compressing 处理的,并且 一定要记得设置中文解码否则可能会出现中文乱码

当然,例如 node-stream-zipextract-zip 也无法解析中文,会出现乱码