前端大文件上传,分片方式上传

前端大文件分片上传

文件上传超时:原因是前端请求框架限制最大请求时长,后端设置了接口访问的超时时间,或者是 nginx(或其它代理/网关) 限制了最大请求时长。
文件大小超限:原因在于后端对单个请求大小做了限制,一般 nginx 和 server 都会做这个限制。
上传时间过久(想想10个g的文件上传,这不得花个几个小时的时间)
由于各种网络原因上传失败,且失败之后需要从头开始
整体思路
前端根据代码中设置好的分片大小将上传的文件切成若干个小文件,分多次请求依次上传,后端再将文件碎片拼接为一个完整的文件,即使某个碎片上传失败,也不会影响其它文件碎片,只需要重新上传失败的部分就可以了。而且多个请求一起发送文件,提高了传输速度的上限。
(前端切片的核心是利用 Blob.prototype.slice 方法,和数组的 slice 方法相似,文件的 slice 方法可以返回原文件的某个切片)
前端代码

<template>
  <div style="height: 100%">
    <!--    test-->
    <!-- 面包屑导航区域 -->
    <el-breadcrumb separator-class="el-icon-arrow-right">
      <el-breadcrumb-item :to="{ path: '/home' }">首页</el-breadcrumb-item>
      <el-breadcrumb-item>大文件上传和下载</el-breadcrumb-item>
      <el-breadcrumb-item>大文件上传</el-breadcrumb-item>
    </el-breadcrumb>
    <!-- 卡片视图区域 -->
    <el-card style="height: 100%">
      <!--行-->
      <el-row>
        <!--列-->
        <el-col style="width:0">
          <el-button type="primary" @click="dialogTableVisible = true">上传文件</el-button>
        </el-col>
      </el-row>
    </el-card>
    <el-dialog title="上传文件" :visible.sync="dialogTableVisible">
      <el-upload
        style="width: 0"
        class="upload-demo"
        action=""
        :before-upload="beforeUpload"
        :multiple="false">
        <el-button size="small" type="primary">点击上传</el-button>
      </el-upload>
      <el-table :data="fileList">
        <el-table-column property="fileName" label="文件名"></el-table-column>
      </el-table>
      <div slot="footer" class="dialog-footer">
        <el-button @click="onCancel">取消</el-button>
        <el-button type="primary" @click="onOk">上传</el-button>
      </div>
    </el-dialog>
  </div>
</template>
<script>
export default {
  data() {
    return {
      dialogTableVisible: false,
      // 列表展示的
      fileList: [],
      // 真正上传的
      uploadList: [],
      size: 50 * 1024 * 1024,
      file: {}
    }
  },
  mounted() {
    this.init()
  },
  methods: {
    // 初始化
    init() {
    },
    // 上传前调用,阻止上传
    beforeUpload(file) {
      this.file = file;
      // 切片生成
      const fileChunkList = this.createFileChunk(file);
      this.uploadList = fileChunkList.map(({file, fileName}, index) => ({
        chunk: file,
        // 这里的hash为文件名 + 切片序号,
        hash: fileName + "-" + index
      }));
      this.fileList = [{
        fileName: file.name
      }];
      return false;
    },
    // 生成切片文件
    createFileChunk(file) {
      const fileChunkList = [];
      let cur = 0;
      // 判断这个文件大小需不需去切割
      while (cur < file.size) {
        fileChunkList.push({
          file: file.slice(cur, cur + this.size),
          fileName: file.name
        });
        cur += this.size;
      }
      return fileChunkList;
    },
    // 调接口的方法  直接返回一个promise对象
    uploadFiles(formData) {
      return this.$axios.post('roles', formData, {headers: {'Content-Type': 'multipart/form-data'}});
    },
    // 取消
    onCancel() {
    },
    // 确认
    async onOk() {
      // 使用表单形式
      const requestList = this.uploadList.map(({chunk, hash}) => {
        const formData = new FormData();
        formData.append("chunk", chunk);
        formData.append("hash", hash);
        formData.append("filename", this.file.name);
        return {formData};
      }).map(async ({formData}) => {
        this.uploadFiles(formData)
      });
      // 将分隔好的文件段一起向后端请求
      await this.$axios.all(requestList)
      // 分段请求后向后端发起合并请求,让后端将文件拼接
      await this.$axios('merge', {
        file: this.file.name,
        size: this.file.size
      });
    }
  }
};
</script>
<style lang="less">
.treeTable {
  margin-top: 15px
}
</style>


  • 1
    点赞
  • 13
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值