基于vue-simple-uploader封装文件分片上传组件
基于vue-simple-uploader封装文件分片上传组件
·
vue-simple-uploader 是基于 simple-uploader.js 和Vue结合做的一个上传组件,自带 UI,可覆盖、自定义。它支持文件、多文件、文件夹上传;支持拖拽文件、文件夹上传;可暂停、继续上传;支持秒传;上传队列管理,支持最大并发上传;分片上传;支持进度、预估剩余时间、出错自动重试、重传等操作。
文档
vue-simple-uploader文档
simple-uploader.js文档
安装
npm install vue-simple-uploader --save
使用
因为我是单独使用的子组件,所以我只在子组件中使用,也可以放在main.js中
import uploader from 'vue-simple-uploader'
import Vue from 'vue'
Vue.use(uploader)

html部分
<template>
<div>
<uploader
ref="simUploader"
:options="options"
:autoStart="autoStart"
:fileStatusText="fileStatusText"
class="uploader"
@file-added="onFileAdded"
@files-added="onFilesAdded"
@file-success="onFileSuccess"
@file-progress="onFileProgress"
@file-error="onFileError"
>
<uploader-unsupport></uploader-unsupport>
<uploader-btn class="uploader-wrapper" :attrs="attrs">
<uploader-drop :attrs="attrs">
<img class="img-upload" :src="require('#/assets/images/icon-upload.png')" alt="" />
<div class="uploader-drop-text">
可直接点击或将文件拖拽至框内上传
</div>
</uploader-drop>
</uploader-btn>
<div class="uploader-list">
<div v-for="(item, index) in uploaderList" :key="item.id + index" class="uploader-list-item">
<uploader-file :file="item" hidden-complete list>
<template #default="{ extension, file, formatedSize, progress, status }">
<div class="uploader-list-item__inner">
<i class="el-icon-document"></i>
<template>
<el-tooltip :content="file.name">
<span class="file-info">
{{ file.name }}
</span>
</el-tooltip>
<md-el-icon
name="el-icon-edit-outline edit-icon"
@click="editFile(index)"
style="font-size:16px"
></md-el-icon>
</template>
<span>{{ formatedSize }}</span>
<div v-if="file.name.split('.').pop() === 'zip' && enableUnzip">
<el-radio-group v-model="file.enableUnzip">
<el-radio :label="true">
<span>解压</span>
</el-radio>
<el-radio :label="false">
<span>不解压</span>
</el-radio>
</el-radio-group>
</div>
<!-- 状态分为2种, 上传,解压 -->
<div style="width:30%">
<div v-if="item.enableUnziping">
<el-progress
:percentage="item.progress"
:status="item.status ? 'exception' : 'success'"
:show-text="false"
text-inside
></el-progress>
</div>
<div v-else>
<el-progress
:percentage="progress * 100"
:status="item.status ? 'exception' : 'success'"
:show-text="false"
text-inside
></el-progress>
</div>
</div>
<div style="width:50px">
{{
item.enableUnzipStatus
? item.enableUnzipStatus
: item.status
? fileStatusText.error
: fileStatusText[status]
}}
</div>
<i
class="el-icon-circle-close"
@click="deleteFile(item, index)"
style="font-size:16px"
></i>
</div>
</template>
</uploader-file>
</div>
</div>
</uploader>
</div>
</template>
配置项
由于是对原simple-upload.js做的封装,大多数的配置可以参考原生js文档,部分配置与原生有些许不同,请注意仔细查看相关参考文档。
options: {
target: this.uploadUrl, //目标上传地址URL,默认值为 '/'。
singleFile: this.singleFile, // 单文件上传 默认 false
chunkSize: 5 * 1024 * 1024, // 分片时按照该值来分。最后一个上传分片的大小是可能是大于等于1倍的这个值但是小于两倍的这个值大小
forceChunkSize: true, // 是否强制所有的块都一定小于等于chunkSize
fileParameterName: 'file', // 上传文件时文件的参数名,默认file
maxChunkRetries: 5, // 最大自动失败重试上传次数,值可以是任意正整数,如果是 undefined 则代表无限次,默认 0
testChunks: true, // 是否开启服务器分片校验
simultaneousUploads: 6, // 并发上传数,默认 3
chunkRetryInterval: null, // 重试间隔,值可以是任意正整数,如果是 null 则代表立即重试,默认null
query: {}, // 其他额外的参数
headers: this.uploadHeaders, // 额外的一些请求头,例如有时我们需要在header中向后台传递token,默认为对象: {}
processParams: (params, file) => {
// 处理请求参数 一般用于修改参数名字或者删除参数 这里我处理了压缩文件是否可以解压
if (this.enableUnzip && file.enableUnzip) {
}
params.md5 = file.md5
params.identifier = file.identifier
return params
}
},
fileStatusText: {
success: '成功',
error: '出错了',
uploading: '上传中',
paused: '暂停',
waiting: '等待中'
},
autoStart:false, // 默认 true, 是否选择文件后自动开始上传。
methods
onFileAdded(file) {
//文件添加成功后可以进行文件格式、文件名称、文件大小、是否可重名等限制
// 计算MD5,完成后开始上传操作
this.computeMD5(file, () => {
this.$emit('addFile', file)
})
}
onFilesAdded(files, fileList) {
console.log('on-files-added', files, fileList)
},
// 文件上传成功后,在“上传完成”的回调中,通过服务端合并文件
onFileSuccess(rootFile, file, message, chunk) {
// 如果 需要合并
if (this.needMerge) {
request.mergerFile({
tempName: res.tempName,
fileName: file.name,
...file.params,
}).then(data => {
// 文件合并成功
this.$emit('fileSuccess', data);
}).catch(e => {});
// 不需要合并
} else {
this.$emit('fileSuccess', res);
console.log('上传成功');
}
},
onFileProgress(rootFile, file, chunk) {
// 文件进度的回调
// console.log('on-file-progress', rootFile, file, chunk)
},
onFileError(rootFile, file, message, chunk) {
console.log('on-file-error', rootFile, file, message, chunk)
},
MD5计算
断点续传及秒传的基础是要计算文件的MD5,这是文件的唯一标识,然后服务器根据MD5进行判断,是进行秒传还是断点续传。在file-added事件之后,就计算MD5,我们最终的目的是将计算出来的MD5加到参数里传给后台,然后继续文件上传的操作,详细的思路步骤是:
1)把uploader组件的autoStart设为false,即选择文件后不会自动开始上传
2)先通过 file.pause()暂停文件,然后通过H5的FileReader接口读取文件
3)将异步读取文件的结果进行MD5,这里我用的加密工具是spark-md5,你可以通过npm install spark-md5 --save来安装,也可以使用其他MD5加密工具。
4)file有个属性是uniqueIdentifier,代表文件唯一标示,我们把计算出来的MD5赋值给这个属性 file.uniqueIdentifier = md5,这就实现了我们最终的目的。
5)通过file.resume()开始/继续文件上传。
/**
* 计算md5,实现断点续传及秒传
* @param file
* @param callback 计算完成的回调
*/
computeMD5(file, callback) {
let fileReader = new FileReader()
let time = new Date().getTime()
let blobSlice = File.prototype.slice || File.prototype.mozSlice || File.prototype.webkitSlice
let currentChunk = 0
const chunkSize = this.options.chunkSize // 10 * 1024 * 1000;
let chunks = Math.ceil(file.size / chunkSize) // 总块数
let spark = new SparkMD5.ArrayBuffer()
file.pause()
file.md5Status = '解析中'
file.temp_identifier = Math.random() // 先添加一个临时的, 前端自己用
loadNext()
fileReader.onload = e => {
spark.append(e.target.result)
if (currentChunk < chunks) {
currentChunk++
loadNext()
} else {
let md5 = spark.end()
file.identifier = this.folderId + '/' + md5
this.computeMD5Success(md5, file)
file.md5Status = '解析完成'
console.log(
`MD5计算完毕:${file.name} \nMD5:${md5} \n分片:${chunks} 大小:${
file.size
} 用时:${new Date().getTime() - time} ms`
)
callback && callback()
}
}
fileReader.onerror = function() {
this.$notify({
title: '错误',
message: `文件${file.name}读取出错,请检查该文件`,
type: 'error',
duration: 2000
})
file.cancel()
}
function loadNext() {
let start = currentChunk * chunkSize
let end = start + chunkSize >= file.size ? file.size : start + chunkSize
fileReader.readAsArrayBuffer(blobSlice.call(file.file, start, end))
}
},
computeMD5Success(md5, file) {
// 将自定义参数直接加载uploader实例的opts上
file.md5UniqueIdentifier = md5
file.isMergeComplete = false
if (this.autoStart) {
// 自动上传
file.resume()
}
},
给file的uniqueIdentifier 属性赋值后,请求中的identifier即是我们计算出来的MD5
更多推荐


所有评论(0)