el-upload分片上传文件

277 阅读2分钟

自定义上传的方式

http-request 自定义上传的方式

<el-upload
  v-show="isInitUploadStatus()"
  ref="qUpload"
  class="upload-main"
  drag
  :auto-upload="false"
  accept=".zip,.bat"
  action="-"
  :http-request="handleUploadFile"
  :headers="headers"
  :show-file-list="false"
  :before-upload="beforeFileUpload"    
>
  <i class="qax-icon-fillupload" />
  <div class="q-upload__text">
    <em>点击/拖拽</em> 上传文件
  </div>
</el-upload>
/**
* 自定义文件上传
*/
async handleUploadFile(params) {
  const chunkSize = 20 * 1024 * 1024; // 每个文件块大小为20MB
  this.chunks = this.splitChunks(chunkSize, params.file);
  // 发送文件块到服务器
  await this.sendService(params);
},
  1. 之所以走,自定义文件上传的方式,是因为el-upload自带的上传方式,是直接上传,无法满足我们的需求。针对大文件的上传,会卡死页面。

  2. 自定义上传的参数,params,包含了所有的单个属性和方法。这里包括:on-success,on-error,on-progress等,使用方式和正常使用相同。只不过触发方式是在自定义函数里边触发;

    1. params.onProgress(); // 调用的方法其实是,模板上定义的方法
      
  3. 下一步,就可以拿到File对象,进行文件切片上传了。

文件切片上传

// 切片
splitChunks(chunkSize, file) {
  let chunks = [];
  let cur = 0;
  if (file.size > chunkSize) {
    while (cur < file.size) {
      chunks.push(file.slice(cur, cur + chunkSize));
      cur += chunkSize;
    }
  } else {
    chunks.push(file);
  }
  return chunks;
},
  1. 大于预定的文件大小,循环截取,储存在数组中。
  2. 小于预定的文件大小,直接存储,准备下一阶段的上传。

文件上传

// 发送到服务端
async sendService({file}) {
  const {name, size} = file;
  const {fileUid} = this.fileConfig;
  const chunksLength = this.chunks.length;
  let num = 0;

  for (let i = 0; i < chunksLength; i++) {
    const item = this.chunks[i];
    try {
      await this.$service.uploadChunkPost({
        uuid: fileUid,
        chunk: i,
        chunks: chunksLength,
        size: size,
        file: item,
        file_name: name,
      });
      num++;
      if (num === chunksLength) { // 用于处理文件全部上传完成,进行合并的调用操作
        await this.mergeFile();
      }
    } catch (error) {
      console.error('Error uploading chunk:', error);
    }
  }
},
  1. uuid,生成一个随机不重复的唯一标识。我用的uuid包。

    1. import { v4 as uuidv4 } from 'uuid';
      // 生成UUIDv4
      const uuid = uuidv4();
      // 转换为字符串并输出长度
      const uuidString = uuid.replaceAll('-', '');
      
  2. 循环分片的数组,调用上传的接口。【这里的代码,这样写感觉不太好】思考: Permison

  3. 这里还有浏览器的请求数量限制, Web Worker

合并文件

async mergeFile() {
  try {
   // 调用文件合并接口
  } catch (error) {
    console.error('Error merging chunks:', error);
  }
},
  1. 如上有一个判断是不是已经上传完成,上传完成之后,就可以调用合并接口了。

代码集合

  1. 就不合并起来了,反而不好看了。这里还有几个问题要思考优化的。Web Worker,循环请求。File对象。Blob。
  2. 上传文件之前的,大小和格式校验是必要的。
  3. 上传会伴随,上传状态,这个也会同步处理。

文件批量上传

自定义请求方式,轮训请求后端接口,可取消接口请求。

输入

获取到文件的fileList,支撑展示

:on-change="handleFileChange" handleFileChange,返回file和fileList

批量上传

const list = this.generatePromiseRequest();
Promise.all(list);
//-----------------------------------------//
// 单独封装上传参数,用于控制取消上传请求
generatePromiseRequest() {
  const promiseList = [];
  for (const element of this.tableData) {
    const item = element;
    const { file } = item;
    // 文件校验
    if (!this.filesCheck(file)) {
      const {uploadEnabledState, uploadErrorMessage} = this.uploadFileCheckMap;
      this.$set(item, 'uploadEnabledState', uploadEnabledState);
      this.$set(item, 'uploadErrorMessage', uploadErrorMessage);
      continue;
    }
    const param = new FormData();
    param.append('file', file.raw);
    const requestPendingList =  this.$http
      .post(
        `url/xxxx/参数`,
        param,
        {
          headers: { 'Content-Type': 'multipart/form-data' },
          // 文件上传进度控制
          onUploadProgress: (e) => {
            const percentage = (e.loaded / e.total) * 100 || 0;
            this.$set(item, 'percentage', percentage);
          },
          // 文件取消
          cancelToken: new CancelToken((c) => {
            item.cancelToken = c;
          }),
        }
      )
      .then((data) => {
        if (data.status !== 10000) {
          throw new Error();
        }
        this.handleSuccess(data, item);
      })
      .catch((e) => {
        this.handleError(e, item);
      });

    promiseList.push(requestPendingList);
  }
  return promiseList;
},

取消文件上传

const CancelToken = axios.CancelToken;
// 文件取消
cancelToken: new CancelToken((c) => {
    item.cancelToken = c;
}),