文件分片上传

文件分片上传

前端

<!DOCTYPE html>
<html lang="en">
<head>
	<meta charset="UTF-8">
	<title>文件上传</title>
</head>
<body>
	<input type="file" id="file-upload" name="">
	<button class="upload-btn" onclick="upload(0)">上传</button>
</body>
<!-- 使用 axios 文件上传 -->
<script src="https://cdn.bootcdn.net/ajax/libs/axios/0.19.2/axios.min.js"></script>
<script>
 const uploadBtn = document.querySelector('.upload-btn')
 const uploadFile = document.querySelector('#file-upload')
 // 设置 分片 大小 1M
 const chunkSize = 1024*1024
 /**
  * [文件分片上传]
  * @param {Number} [index] [分片标号]
  * @return {[type]} [description]
  */
 function upload(index) {
 	// 获取文件上传表单
 	try {
 		const file = uploadFile.files[0]

 		// 解构 获取 原文件名以及 后缀
 		let [fName, suffix] = file.name.split('.')
 		// 文件分片
 		let single = index * chunkSize
 		// 如果 字节超过 则停止上传
 		if(single > file.size) {
 			merge(file.name)
 			return false
 		}
 		let block = file.slice(single, single + chunkSize)
 		// 设置分片的 文件名 文件名 . 索引 . 后缀
 		let blockName = `${fName}.${index}.${suffix}`
 		let blockFile = new File([block], blockName)

	 	let formData = new FormData()
	 	formData.append('file', blockFile)
	 	// 发送请求
	 	axios.post('/upload', formData).then((res) => {
	 		if(res.status === 200) {
	 			console.log(res)
	 			// 递归调用 上传
	 			upload(++index)
	 		}
	 		
	 	})
 	} catch (e) {
 		console.log(e)
 	}
 }
 /**
  * [merge 合并分片文件]
  * @param  {[String]} name [上传文件名]
  * @return {[String]}      [文件上传 后 url]
  */
 function merge(name) {
 	try {
 		axios.post('/merge', {name: name}).then(res => {
 			if(res === 200) {
 				return res.url
 			} else {
 				console.error('文件上传失败')
 			}
 		})
 	} catch(e) {
 		console.log(e)
 	}
 }
</script>
</html>

后端

const express = require('express')
const bodyParser = require('body-parser')
const multiparty = require('multiparty')
const fse = require('fs-extra')
const path = require('path')
const fs = require('fs')

const app = express()

app.use(express.static(__dirname + '/public'))

app.use(bodyParser.urlencoded({extended: true}))

app.use(bodyParser.json())

const UPLOAD_DIR = path.resolve(__dirname, 'public/upload')

// 文件上传路由
app.post('/upload', (req, res) => {
	const form = new multiparty.Form({uploadDir: 'temp'})
	form.parse(req)
	form.on('file',async (name, chunk) => {
		console.log(chunk)
		// 存放 切片文件的目录
		let chunkDir = `${UPLOAD_DIR}/${chunk.originalFilename.split('.')[0]}`
		// 如果目录不存在 创建目录
		if(!fse.existsSync(chunkDir)) {
			await fse.mkdirs(chunkDir)
		}
		// 源文件名.index.suffix
		var dPath = path.join(chunkDir, chunk.originalFilename.split('.')[1])
		// 将分片从临时目录移动到 同名的存放目录
		await fse.move(chunk.path, dPath, {overwrite: true})
	})

	res.send('文件上传成功')
})

// 合并分片文件
app.post('/merge', async (req, res) => {
	let { name } = req.body
	// 文件名分割 不要后缀
	let fName = name.split('.')[0]
	// 合并 路径
	let chunkDir = path.join(UPLOAD_DIR, fName)
	// 读取 分片文件 索引 数组
	let chunks = await fse.readdir(chunkDir)
	// 按照索引过合并文件
	chunks.sort((a, b) => a - b ).map(chunkPath => {
		fs.appendFileSync(
			path.join(UPLOAD_DIR, name),// 合并的文件
			fs.readFileSync(`${chunkDir}/${chunkPath}`) // 分片遍历 按照索引 写入 
		)
	})
	// 移出 原来的分片 目录
	fse.removeSync(chunkDir)
	res.send({
		msg: '文件合并成功',
		url: `http://localhost:3000/upload/${name}`
	})
})

// 端口监听
app.listen(3000, () => {
	console.log('port: 3000')
})

参考摘自 :B站itvlog
项目git 地址

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值