ElementUI el-upload 断点续传文件

官方的 Upload 组件从文档和所有demo来看,均是选中文件直接上传,但是业务系统有大文件上传的需求,所以要用这个组件封装一个断点续传的功能。

从官方给出的文档看到有个 http-request 覆盖默认的上传行为,可以自定义上传的实现 似乎能满足要求,那就开撸。

确定需求:最大支持2GB的任意文件上传,小于100M直接上传,大于100M的时候分块上传,并且要支持断点续传。

我拿了官方的一个demo

 <el-upload
      drag
      multiple
      :http-request="checkedFile"
      action="/"
    >
      <i class="el-icon-upload"></i>
      <div class="el-upload__text">将文件拖到此处,或<em>点击上传</em></div>
    </el-upload>

可拖动上传,比较高大上一点。

http-request 方法定义之后,文件上传会先走这个方法,传入一个参数

options = {
	headers: this.headers,
	withCredentials: this.withCredentials,
	file: rawFile,
	data: this.data,
	filename: this.name,
	action: this.action,
	onProgress: e => {
	  this.onProgress(e, rawFile);
	},
	onSuccess: res => {
	  this.onSuccess(res, rawFile);
	  delete this.reqs[uid];
	},
	onError: err => {
	  this.onError(err, rawFile);
	  delete this.reqs[uid];
	}
}

该参数就是组件的参数集合,同时,如果定义了这个方法,组件的submit方法就会被拦截掉(注意别在这个方法里面调用组件的submit 方法,会造成死循环),在这个方法里面我就可以搞我想搞的事情了。

说一下要注意的:
使用这个断点续传方法一定要先和服务端协调好,看他们怎么处理的,比如我这里就是按照文件分块后按照序号和文件id等信息跟服务端建立联系,服务端从接收到第一块文件的请求开始就会检测该文件是否已经存在已接收的文件块,然后再返回续传的块的序号,最终再调用接口校验文件完不完整。

如果使用mock来模拟接口的话,onUploadProgress是无效的,因为mock会重新声明一个XMLHttpRequest,不会继承onUploadProgress

以上,就是el-upload组件的大文件分块上传的改造方案,目前还很粗糙,甚至还没过测试,如有问题会持续更新

20180726
看源码的时候发现http-request 这个传入的回调函数应该返回一个Promise

  const req = this.httpRequest(options);
  this.reqs[uid] = req;
  if (req &a
在使用 `WebUploader` 实现大文件分块上传断点续传和秒传功能时,结合 `Vue` 和 `Element UI` 的 `el-upload` 组件,我们需要在前端处理文件的分片、上传逻辑,并与后端进行交互。以下是一个完整的实现示例,包括详细的注释说明。 ### 一、准备工作 1. 安装 WebUploader: WebUploader 是百度开源的上传组件,可以通过 CDN 或 npm 安装。这里我们使用 CDN 方式引入。 2. 安装 Element UI: ```bash npm install element-ui --save ``` 3. 引入 Element UI: ```javascript import ElementUI from 'element-ui' import 'element-ui/lib/theme-chalk/index.css' Vue.use(ElementUI) ``` --- ### 二、Vue 组件代码(使用 WebUploader 实现分片上传) ```vue <template> <div> <el-upload ref="uploader" action="http://your-api.com/upload" <!-- 后端接口地址 --> :before-upload="beforeUpload" :http-request="uploadFile" :on-success="handleSuccess" :on-error="handleError" :limit="1" accept="*" > <el-button type="primary">选择文件</el-button> </el-upload> </div> </template> <script> import SparkMD5 from 'spark-md5' // 用于计算文件 md5,实现秒传 export default { name: 'ChunkUploader', data() { return { chunkSize: 1024 * 1024 * 2, // 每个分片大小 2MB chunks: [], // 分片列表 fileMd5: '', // 文件 md5 } }, methods: { // 文件选择后触发 beforeUpload(file) { this.file = file return new Promise((resolve, reject) => { this.calculateMD5(file).then(md5 => { this.fileMd5 = md5 // 请求后端判断是否已存在该文件(秒传) this.checkFileExist(md5).then(exist => { if (exist) { this.$message.success('该文件已存在,秒传成功') reject(false) } else { resolve(true) } }) }) }) }, // 计算文件 md5 calculateMD5(file) { return new Promise((resolve, reject) => { const spark = new SparkMD5.ArrayBuffer() const reader = new FileReader() const chunkSize = this.chunkSize let currentChunk = 0 reader.onload = e => { spark.append(e.target.result) currentChunk++ if (currentChunk < file.size / chunkSize + 1) { loadNext() } else { resolve(spark.end()) } } reader.onerror = () => { reject('读取文件失败') } const loadNext = () => { const start = currentChunk * chunkSize const end = Math.min(file.size, start + chunkSize) reader.readAsArrayBuffer(file.slice(start, end)) } loadNext() }) }, // 检查文件是否已存在(用于秒传) checkFileExist(md5) { return new Promise(resolve => { // 模拟请求后端接口 setTimeout(() => { resolve(false) // 假设不存在 }, 500) }) }, // 自定义上传函数(分片上传uploadFile(options) { const file = options.file const chunks = Math.ceil(file.size / this.chunkSize) for (let i = 0; i < chunks; i++) { const start = i * this.chunkSize const end = Math.min(file.size, start + this.chunkSize) const chunk = file.slice(start, end) this.uploadChunk(chunk, i, chunks, file.name) } }, // 上传单个分片 uploadChunk(chunk, index, total, filename) { const formData = new FormData() formData.append('file', chunk) formData.append('index', index) formData.append('total', total) formData.append('filename', filename) formData.append('md5', this.fileMd5) // 模拟上传 setTimeout(() => { console.log(`第 ${index + 1} 个分片上传完成`) if (index === total - 1) { this.mergeChunks(filename, total) } }, 500) }, // 所有分片上传完成后合并 mergeChunks(filename, total) { // 请求后端合并分片 setTimeout(() => { this.$message.success('文件上传成功') }, 500) }, // 上传成功回调 handleSuccess(response, file, fileList) { console.log('上传成功', response) }, // 上传失败回调 handleError(error, file, fileList) { this.$message.error('上传失败') } } } </script> ``` --- ### 三、代码说明 1. **`beforeUpload`**: -上传前计算文件的 MD5,用于实现秒传功能。 - 请求后端检查是否已存在该文件,若存在则直接返回成功。 2. **`calculateMD5`**: - 使用 `spark-md5` 库分片读取文件并计算其 MD5。 - 避免一次性读取大文件导致内存溢出。 3. **`uploadFile`**: -文件切分为多个 `Blob` 分片。 - 调用 `uploadChunk` 上传每个分片。 4. **`uploadChunk`**: - 构造 `FormData` 包含分片信息(index、total、filename、md5)。 - 模拟上传行为,实际应使用 `axios` 或 `fetch` 发送请求。 5. **`mergeChunks`**: - 所有分片上传完成后请求后端合并分片文件--- ### 四、相关问题
评论 17
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值