vue实现切片上传
切片上传实现原理
切片上传的核心是将大文件分割为多个小块(chunk),分批上传到服务器。服务器接收后合并这些切片,最终还原完整文件。这种方式能有效解决大文件上传超时、网络不稳定等问题,并支持断点续传。
前端实现步骤
HTML部分 创建文件选择控件和上传按钮,监听文件选择事件:
<input type="file" @change="handleFileChange" />
<button @click="handleUpload">上传</button>
定义切片方法
通过Blob.prototype.slice方法实现文件分片:
function createChunks(file, chunkSize) {
const chunks = []
let start = 0
while (start < file.size) {
chunks.push({
chunk: file.slice(start, start + chunkSize),
filename: file.name
})
start += chunkSize
}
return chunks
}
上传逻辑实现 使用FormData携带切片数据,注意添加切片序号和文件hash标识:
async function uploadChunks(chunks) {
const requests = chunks.map((chunk, index) => {
const formData = new FormData()
formData.append('chunk', chunk.chunk)
formData.append('filename', chunk.filename)
formData.append('hash', `${fileHash}-${index}`)
return axios.post('/upload', formData)
})
await Promise.all(requests)
}
后端处理要点
接收切片 使用multer等中间件处理文件上传:
app.post('/upload', upload.single('chunk'), (req, res) => {
const { filename, hash } = req.body
fs.renameSync(req.file.path, `temp/${hash}`)
res.send({ status: 'success' })
})
合并切片 当所有切片上传完成后触发合并操作:
function mergeFiles(filename, chunkCount) {
const writeStream = fs.createWriteStream(`uploads/${filename}`)
for (let i = 0; i < chunkCount; i++) {
const chunkPath = `temp/${fileHash}-${i}`
writeStream.write(fs.readFileSync(chunkPath))
fs.unlinkSync(chunkPath)
}
writeStream.end()
}
优化功能实现
文件哈希生成 使用spark-md5计算文件唯一标识:
function calculateHash(file) {
return new Promise(resolve => {
const spark = new SparkMD5.ArrayBuffer()
const reader = new FileReader()
reader.onload = e => {
spark.append(e.target.result)
resolve(spark.end())
}
reader.readAsArrayBuffer(file)
})
}
断点续传支持 上传前检查服务器已存在的切片:
async function checkExistingChunks(fileHash, chunkCount) {
const { data } = await axios.get('/check', {
params: { fileHash, chunkCount }
})
return data.existedChunks
}
进度显示 利用axios的onUploadProgress回调更新进度:
axios.post('/upload', formData, {
onUploadProgress: progressEvent => {
const percent = Math.round(
(progressEvent.loaded / progressEvent.total) * 100
)
console.log(`当前切片上传进度:${percent}%`)
}
})
完整示例组件
<template>
<div>
<input type="file" @change="handleFileChange" />
<button @click="handleUpload">上传</button>
<div>进度:{{ progress }}%</div>
</div>
</template>
<script>
import axios from 'axios'
import SparkMD5 from 'spark-md5'
export default {
data() {
return {
file: null,
progress: 0
}
},
methods: {
handleFileChange(e) {
this.file = e.target.files[0]
},
async handleUpload() {
if (!this.file) return
const fileHash = await this.calculateHash(this.file)
const chunks = this.createChunks(this.file, 1024 * 1024) // 1MB每片
const existedChunks = await this.checkExistingChunks(fileHash, chunks.length)
const requests = chunks
.filter((_, index) => !existedChunks.includes(index))
.map((chunk, index) => this.uploadChunk(chunk, fileHash, index))
await Promise.all(requests)
await this.mergeFiles(fileHash, this.file.name)
},
// 其他方法实现参考前述代码片段
}
}
</script>





