1、大文件上传直接传的弊端?

大文件上传,当大文件上传时耗费时间长,有被中断的可能性,当被中断后,无论前面上传进度是多少,都会归0,无法实现断点续传

2、分片上传的好处?

分片上传:分片上传就是把要上传的大文件,按照某个大小进行切片,把切片文件逐一上传,在这里需要后端的配合,给前端分片上传和上传完合并的接口

3、分片上传原理?

分片上传把大文件变成了一个个的小文件上传,可以提升速度,如果中途中断,和后端配合设好唯一标识,下次上传同一个文件时根据文件本身的信息,例如文件名、文件大小等进行判断,如果新上传的文件和之前上传中断的文件是同一个文件时,可以从上传中断的分片继续上传,节约时间,从而实现断点续传

4、前端需要做的部分?

前端所需要做的工作是把文件进行分片,把分片逐一上传,并在最后一个切片上传完毕后请求后端进行文件合并

5、何时开始分片?

当选择完文件,拿到文件信息,就可以进行分片了

6、具体实现

html内容:

<div class="uploader">
    <ul class="list">
        <li class="list__item" v-for="(item, index) in fileList" :key="item.url">
            <span>
                <i class="icon-icon-" />
                <span
                class="list__item-name"
                :title="item.name"
                >{{ interceptStr(item.name) }}
                </span>
            </span>
            <i class="icon-guanbi" @click="cancel({ item, index })" />
        </li>
    </ul>
    <p class="uploader__upload">
        <i class="icon-jiahao" /> 上传文件
        <input
            :accept=""
            type="file"
            class="uploader__input"
            @click="clearValue"
            @change="selectFile"
        />
    </p>
</div>

js内容:

//创建一个文件对象
let files = {}
//文件上传列表数据
let fileArray=''
//当前上传坐标
let pareIndex = 0
//请求接口所需参数
let tempFile=''
let tempFile=''
let tempObject=''
let id=''
let modelUuid=''

//上传文件列表
const fileList = computed(() => {
  return Array.isArray(fileArray)
    ? fileArray
    : !fileArray
    ? []
    : [fileArray]
})

//清空input的value
const clearValue = e => {
    e.target.value = ''
}
const selectFile = e =>{
    //获取所选文件信息
    files = e.target.files[0]
    //形成文件列表
    const { name } = files 
    fileArray={name }
    //请求文件分片
    uploadFileSilce(files, pareIndex)
}
//请求合并文件
const mergeFile=(pareTotal)=>{
    let params={
        folderName:tempFile,
        objectName:tempObject,
        partNum:pareTotal,
        stationModelId:id
    }
    //请求后端合并接口
    modelServe.mergeUpload(params).then(res => {})
}
//文件切片
const uploadFileSilce = (file, pareIndex) => {
      // 文件名
      const { name } = file;
      //文件分片后,每一个分片的名字,按照坐标逐一递增
      let portFileName=file.name.substring(0,file.name.lastIndexOf("."))+'_'+`${pareIndex+1}`+file.name.substring(file.name.lastIndexOf("."))
      // 文件大小
      const { size } = file;
      // 分片大小(50M,可以自行设置)
      const pareSize = 1024 * 1024 * 50;
      // 分片总数
      const pareTotal = Math.ceil(size / pareSize);
      // 如果 当前分片索引 大于 总分片数
      if (pareIndex >= pareTotal) {
        // 合并文件
        mergeFile(pareTotal);
        return;
      }
      // 文件开始结束的位置
      const start = pareIndex * pareSize;
      const end = Math.min(start + pareSize, size);
      // 开始切割
      const packet = new File([file.slice(start, end)], portFileName)      
      // 拼接请求参数
      const formData = new FormData();
      formData.append('files', packet);
      formData.append('partFileIndex', pareIndex+1)//当前分片下标
      formData.append('partFileSum', pareTotal)//总分片数
      formData.append('uuid', modelUuid)//文件uuid,(这里是跟后端获取的,根据自己需求修改)
      formData.append('filesName', portFileName)//分片文件名称
      formData.append('stationModelId', id)//id
      formData.append('fileSize', file.size)//大小这里是跟后端获取的,根据自己需求修改)        
      // 如果 当前分片索引 小于 总分片数
      if (pareIndex < pareTotal) {
        //请求后端分片接口
        modelServe.portUpload(formData).then(res => {
            const { code, msg, data } = res.data
            if (code === 0) {
                pareIndex++;
	            //准备合并所需要的参数
                tempFile=res.data.data.folderName
                tempObject=res.data.data.objectName
                // 递归调用 分片函数
                uploadFileSilce(file, pareIndex);
            }
        })
      }
};

css内容:

.uploader {
  width: 100%;
  &__upload {
    // background: #f2f3f5;
    // border-radius: 2px;
    border: 1px solid #e5e6eb;
    height: 0.161rem;
    min-height: 25px;
    width: 100%;
    position: relative;
    text-align: center;
    line-height: 0.161rem;
    color: #fff;
    width: 104px;
    height: 32px;
    background: #3491FA;
    border-radius:4px;
    opacity: 1;

    > input {
      position: absolute;
      opacity: 0;
      left: 0;
      right: 0;
      height: 100%;
      font-size: 0;
      cursor: pointer;
    }
  }
  &__input{
    color: #fff;
    width: 104px;
    height: 32px;
    background: #3491FA;
    border-radius: 4px 4px 4px 4px;
    opacity: 1;
  }
}
.list {
  display: flex;
  flex-direction: column;
  &__item {
    background: #e6effe;
    border-radius: 2px;
    height: 0.161rem;
    min-height: 25px;
    width: 100%;
    display: flex;
    align-items: center;
    justify-content: space-between;
    font-size: 14px;
    font-family: SourceHanSansCN-Regular, SourceHanSansCN;
    font-weight: 400;
    color: #165dff;
    margin-bottom: 10px;
    white-space: nowrap;
    padding: 0 0.056rem;

    &-name {
      margin-left: 0.056rem;
      cursor: pointer;
    }
  }
  .icon-guanbi {
    cursor: pointer;
  }
}

感谢BreenCL博主的分享,感谢其他伙伴分享的大文件分片文章,学习到了许多,都写的很详细,如果大家有需要可以移步查看。

GitHub 加速计划 / vu / vue
207.54 K
33.66 K
下载
vuejs/vue: 是一个用于构建用户界面的 JavaScript 框架,具有简洁的语法和丰富的组件库,可以用于开发单页面应用程序和多页面应用程序。
最近提交(Master分支:2 个月前 )
73486cb5 * chore: fix link broken Signed-off-by: snoppy <michaleli@foxmail.com> * Update packages/template-compiler/README.md [skip ci] --------- Signed-off-by: snoppy <michaleli@foxmail.com> Co-authored-by: Eduardo San Martin Morote <posva@users.noreply.github.com> 4 个月前
e428d891 Updated Browser Compatibility reference. The previous currently returns HTTP 404. 5 个月前
Logo

旨在为数千万中国开发者提供一个无缝且高效的云端环境,以支持学习、使用和贡献开源项目。

更多推荐