代码仅供参考,请根据项目实际需求调整。

阿里云OSS分片上传 官方文档 点击前往

后端(JAVA)代码

controller 代码

import com.aliyun.oss.model.PartETag;
import com.xxx.config.ErrorCode; // 只是个错误码而已就不贴了
import com.xxx.model.api.ResultBean;
import com.xxx.model.bean.upload.AliyunPartETag;
import com.xxx.service.AliIOssService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.multipart.MultipartFile;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

@Slf4j
@RestController
@RequestMapping("/upload")
public class UploadController {

    @Autowired
    private AliIOssService aliIOssService;
    
    /**
     * 删除已上传的文件
     *
     * @param fileName 文件名
     * @return
     */
    @PostMapping("/delete/file")
    public ResultBean deleteFile(@RequestParam("fileName") String fileName) {
        ResultBean ret = new ResultBean();
        aliIOssService.deleteOssObject(fileName);
        log.info("delete file:{}", fileName);
        return ret;
    }

    /**
     * 初始化分片上传
     *
     * @return fileName 生成的文件名
     */
    @PostMapping("/part/init")
    public ResultBean initUploadPart(@RequestParam("fileName") String fileName) {
        ResultBean ret = new ResultBean();

        String extensionName = getExtensionName(fileName);
        ArrayList array = new ArrayList<String>();
        array.add("mp4");

        if (!array.contains(extensionName)) {
            ret.setError(ErrorCode.PARA_IS_ERROR, "文件格式不正确");
            return ret;
        }
        //修改原文件名
        String name = System.currentTimeMillis() + "." + extensionName;
        //初始化分片上传
        String uploadId = aliIOssService.initPartUpload(name);
        ret.addEntry("uploadId", uploadId);
        ret.addEntry("fileName", name);

        log.info("upload part init sourceFileName:{}, currentFileName:{}, uploadId:{}", fileName, name, uploadId);

        return ret;
    }

    /**
     * 上传分片
     *
     * @param file
     * @param partNumber 当前文件为第几片
     * @param uploadId   uploadId它是分片上传事件的唯一标识,可以根据这个ID来发起相关的操作,如取消分片上传、查询分片上传等。
     * @param fileName   文件名
     * @param partSize   分片大小
     * @return partETag 前端记得存起来,合并分片的时候要用
     */
    @PostMapping("/part/{partNumber}")
    public ResultBean UploadPart(MultipartFile file, @PathVariable("partNumber") Integer partNumber,
                                 @RequestParam("uploadId") String uploadId, @RequestParam("name") String fileName,
                                 @RequestParam("partSize") Long partSize) {
        ResultBean ret = new ResultBean();

        log.info("upload part start ,uploadId: {}, fileName:{}, partNumber:{}, partSize:{}", uploadId, fileName, partNumber, partSize);
        
        PartETag partETag = aliIOssService.PartUploader(file, fileName, uploadId, partNumber, partSize);
        Map map = new HashMap<String, Object>();
        map.put("partNumber", partETag.getPartNumber());
        map.put("partSize", partETag.getPartSize());
        map.put("eTag", partETag.getETag());
        //long 类型过长传(短点就没问题)到前端会出bug(这个bug必现),故转为String。bug 如下
        //后端传出的值:8677056718873628943, 前端接收的值: 8677056718873629000
        map.put("partCRC", partETag.getPartCRC().toString());
        ret.addEntry("partETag", map);

        return ret;
    }

    /**
     * 合并分片
     *
     * @param uploadId        uploadId它是分片上传事件的唯一标识,可以根据这个ID来发起相关的操作,如取消分片上传、查询分片上传等。
     * @param fileName        文件名
     * @param aliyunPartETags 需要提供所有有效的partETags。OSS收到提交的partETags后,会逐一验证每个分片的有效性。当所有的数据分片验证通过后,OSS将把这些分片组合成一个完整的文件。
     * @return url 文件url
     */
    @PostMapping("/part/merge")
    public ResultBean UploadPartMerge(@RequestParam("uploadId") String uploadId, @RequestParam("fileName") String fileName, @RequestBody List<AliyunPartETag> aliyunPartETags) {
        ResultBean ret = new ResultBean();

        List<PartETag> partETags = new ArrayList<PartETag>();
        aliyunPartETags.forEach(item -> {
            PartETag partETag = new PartETag(item.getPartNumber(), item.geteTag(), item.getPartSize(), item.getPartCRC());
            partETags.add(partETag);
            log.info("part merge message, Number:{}, ETag:{}, Size:{}, CRC:{}", item.getPartNumber(), item.geteTag(), item.getPartSize(), item.getPartCRC());
        });

        aliIOssService.PartMerge(fileName, uploadId, partETags);
        ret.addEntry("url", aliIOssService.getImgUrl() + fileName);

        return ret;
    }

    /**
     * 取消分片上传 并删除所有已上传的分片
     *
     * @param fileName 文件名
     * @param uploadId
     * @return
     */
    @PostMapping("/delete/part")
    public ResultBean deletePart(@RequestParam("fileName") String fileName, @RequestParam("uploadId") String uploadId) {
        ResultBean ret = new ResultBean();
        aliIOssService.abortPartUpload(fileName, uploadId);
        log.info("delete part fileName:{}, uploadId:{}", fileName, uploadId);
        return ret;
    }


    private String getExtensionName(String fileName) {
        if (!(null == fileName) || ("".equals(fileName))) {
            int dot = fileName.lastIndexOf('.');
            if ((dot > -1) && (dot < fileName.length() - 1)) {
                return fileName.substring(dot + 1).trim();
            }
        }
        return "";
    }
}

AliIOssService代码

import com.aliyun.oss.OSS;
import com.aliyun.oss.OSSClient;
import com.aliyun.oss.model.*;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;
import org.springframework.web.multipart.MultipartFile;

import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.util.List;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

@Slf4j
@Service
public class AliIOssService{
	
	//改成你自己的
    @Value("${oss.end_point}")
    private String endpoint;

    @Value("${oss.access_key}")
    private String accessKey;

    @Value("${oss.secret_key}")
    private String secretKey;

    @Value("${oss.bucket_name}")
    private String bucketName;

    public String getImgUrl() {
        return imgUrl;
    }

    public void putOssObject(String objectName, byte[] bytes) {
        OSSClient ossClient = new OSSClient(endpoint, accessKey, secretKey);
        ossClient.putObject(bucketName, objectName, new ByteArrayInputStream(bytes));
        ossClient.shutdown();
    }

    public void deleteOssObject(String objectName) {
        OSSClient ossClient = new OSSClient(endpoint, accessKey, secretKey);
        try {
            ossClient.deleteObject(bucketName, objectName);
        } catch (Exception e) {
            log.error("deleteOssObject, failed to delete object:{}, exception:{}.", objectName, e.getLocalizedMessage());
        }
        ossClient.shutdown();
        return;
    }

    /**
     * 初始分片上传
     *
     * @param objectName 文件名
     * @return uploadId 它是分片上传事件的唯一标识,可以根据这个ID来发起相关的操作,如取消分片上传、查询分片上传等。
     */
    public String initPartUpload(String objectName) {
        OSSClient ossClient = new OSSClient(endpoint, accessKey, secretKey);
        InitiateMultipartUploadRequest request = new InitiateMultipartUploadRequest(bucketName, objectName);
        InitiateMultipartUploadResult result = ossClient.initiateMultipartUpload(request);
        ossClient.shutdown();
        // 返回uploadId,它是分片上传事件的唯一标识,可以根据这个ID来发起相关的操作,如取消分片上传、查询分片上传等。
        return result.getUploadId();
    }

    /**
     * 分片上传
     *
     * @param file       分片好的文件
     * @param objectName 文件名
     * @param uploadId
     * @param partNumber 当前文件是第几片
     * @param partSize   分片大小
     * @return
     */
    public PartETag PartUploader(MultipartFile file, String objectName, String uploadId, Integer partNumber, Long partSize) {
        // 创建OSSClient实例。
        OSSClient ossClient = new OSSClient(endpoint, accessKey, secretKey);
        PartETag partETag = null;
        try {
            // 创建UploadPartRequest,上传片块
            UploadPartRequest uploadPartRequest = new UploadPartRequest();
            uploadPartRequest.setBucketName(bucketName);
            uploadPartRequest.setKey(objectName);
            uploadPartRequest.setUploadId(uploadId);
            uploadPartRequest.setInputStream(file.getInputStream());
            // 设置分片大小。
            uploadPartRequest.setPartSize(partSize);
            // 设置分片号。每一个上传的分片都有一个分片号,取值范围是1~10000,如果超出这个范围,OSS将返回InvalidArgument的错误码。
            uploadPartRequest.setPartNumber(partNumber);
            // 每个分片不需要按顺序上传,甚至可以在不同客户端上传,OSS会按照分片号排序组成完整的文件。
            UploadPartResult uploadPartResult = ossClient.uploadPart(uploadPartRequest);

            partETag = uploadPartResult.getPartETag();
            log.info("part upload success, uploadId:{}, Number:{}, Size:{}, ETag:{}, CRC:{},", uploadId, partETag.getPartNumber(), partETag.getPartSize(), partETag.getETag(), partETag.getPartCRC());
        } catch (Exception e) {
            log.error("upload part error ,uploadId: {}, fileName:{}, partNumber:{}, partSize:{}, Exception:{}", uploadId, objectName, partNumber, partSize, e);
        } finally {
            if (ossClient != null) {
                ossClient.shutdown();
            }
        }
        return partETag;
    }

    /**
     * 分片合并
     *
     * @param objectName 文件名
     * @param uploadId
     * @param partETags
     */
    public void PartMerge(String objectName, String uploadId, List<PartETag> partETags) {
        // 创建OSSClient实例。
        OSSClient ossClient = new OSSClient(endpoint, accessKey, secretKey);
        // 获取已经上传的分片
        /*ListPartsRequest listPartsRequest = new ListPartsRequest(bucketName, fileName, uploadId);
        PartListing partListing = ossClient.listParts(listPartsRequest);
        int partCount = partListing.getParts().size();
        for (int i = 0; i < partCount; i++) {
            PartSummary partSummary = partListing.getParts().get(i);
            System.out.println("\tPart#" + partSummary.getPartNumber() + ", ETag=" + partSummary.getETag());
            //PartETag partETag = new PartETag(partSummary.getPartNumber(), partSummary.getETag());
            //partETags.add(partETag);
        }*/

        // 在执行该操作时,需要提供所有有效的partETags。OSS收到提交的partETags后,会逐一验证每个分片的有效性。当所有的数据分片验证通过后,OSS将把这些分片组合成一个完整的文件。
        CompleteMultipartUploadRequest completeMultipartUploadRequest =
                new CompleteMultipartUploadRequest(bucketName, objectName, uploadId, partETags);

        ossClient.completeMultipartUpload(completeMultipartUploadRequest);
        ossClient.shutdown();
        //合并成功
        log.info("part merge success fileName:{}", objectName);
    }

    /**
     * 取消分片上传 并删除已上传的分片
     *
     * @param objectName 文件名
     * @param uploadId
     */
    public void abortPartUpload(String objectName, String uploadId) {
        OSSClient ossClient = new OSSClient(endpoint, accessKey, secretKey);

        // 取消分片上传,其中uploadId源自InitiateMultipartUpload。
        AbortMultipartUploadRequest abortMultipartUploadRequest = new AbortMultipartUploadRequest(bucketName, objectName, uploadId);
        ossClient.abortMultipartUpload(abortMultipartUploadRequest);
        ossClient.shutdown();
    }

}

ResultBean 代码

import java.util.HashMap;
import java.util.Map;

public class ResultBean {
    public static final int SUCCESS = 0;
    public static final int FAILED = 1;


    private int retcode;
    private String text;
    private Map<String, Object> value = new HashMap<String, Object>();

    public ResultBean() {
        this.retcode = ResultBean.SUCCESS;
        this.text = "";
    }

    public ResultBean(int code, String msg) {
        this.retcode = code;
        this.text = msg;
    }

    public static ResultBean success(){
        return new ResultBean();
    }

    public static ResultBean failed(int code, String msg){
        return new ResultBean(code, msg);
    }

    public int getRetcode() {
        return retcode;
    }

    public void setRetcode(int retcode) {
        this.retcode = retcode;
    }

    public String getText() {
        return text;
    }

    public void setText(String text) {
        this.text = text;
    }

    public Map<String, Object> getValue() {
        return value;
    }

    public void setValue(Map<String, Object> value) {
        this.value = value;
    }

    public void addEntry(String key, Object object) {
        this.value.put(key, object);
    }

    public void setError(int retcode, String text){
        this.retcode = retcode;
        this.text = text;
    }
}

AliyunPartETag 代码

public class AliyunPartETag {

    /**
     * The result information for a part's upload in a multipart upload.
     */
    private static final long serialVersionUID = 2471854027355307627L;

    private int partNumber;
    private String eTag;
    private long partSize;
    private Long partCRC;

    public AliyunPartETag() {

    }

    /**
     * Constructor
     *
     * @param partNumber Part number.
     * @param eTag       Part ETag.
     */
    public AliyunPartETag(int partNumber, String eTag) {
        this.partNumber = partNumber;
        this.eTag = eTag;
    }

    /**
     * Constructor
     *
     * @param partNumber Part number.
     * @param eTag       Part ETag.
     * @param partSize   Part Size.
     * @param partCRC    Part's CRC value.
     */
    public AliyunPartETag(int partNumber, String eTag, long partSize, Long partCRC) {
        this.partNumber = partNumber;
        this.eTag = eTag;
        this.partSize = partSize;
        this.partCRC = partCRC;
    }

    /**
     * Gets part number.
     *
     * @return Part number.
     */
    public int getPartNumber() {
        return partNumber;
    }

    /**
     * Sets part number.
     *
     * @param partNumber Part number.
     */
    public void setPartNumber(int partNumber) {
        this.partNumber = partNumber;
    }


    public String geteTag() {
        return eTag;
    }

    public void seteTag(String eTag) {
        this.eTag = eTag;
    }

    public long getPartSize() {
        return partSize;
    }

    public void setPartSize(long partSize) {
        this.partSize = partSize;
    }

    public Long getPartCRC() {
        return partCRC;
    }

    public void setPartCRC(Long partCRC) {
        this.partCRC = partCRC;
    }

    @Override
    public int hashCode() {
        final int prime = 31;
        int result = 1;
        result = prime * result + ((eTag == null) ? 0 : eTag.hashCode());
        result = prime * result + partNumber;
        return result;
    }

}

前端(VUE)代码

注:前端用的是Element ui

          <el-upload
            ref="uploadVideo"
            name="file"
            list-type="mp4"
            accept=".mp4"
            action
            :file-list="fileVideoList"
            :http-request="handlePartUpload"
            :on-remove="removeVideo"
            :auto-upload="true"
            :limit="1"
            style="width: 400px"
          >
            <el-button size="small" type="primary">点击上传</el-button>
            <div slot="tip" class="el-upload__tip">仅支持上传mp4文件(小于500M)</div>
          </el-upload>
          <div
            v-show="updateLoading"
            style="position: relative; font-size: 12px; margin-top: 10px; z-index: 10000; color:red"
          >
            在上传完成之前请勿刷新或关闭当前页面
          </div>

## import 代码
import { partUpload } from '@/utils/partUpload' //代码在下面(改成自己的路径)
import {
  fetchRemoveFile,
  fetchRemovePart
} from '@/api/partUpload' //代码在下面(改成自己的路径)

## data 代码

      updateLoading: false,
      fileUploadData: null,
      // 上传进度数据
      uploadProgress: {
        fileSize: 0, // 上传文件的总大小 单位:k
        uploadSize: 0, // 已上传文件的大小 单位:k
        status: 0 // 上传的状态 0 无上传文件,1 上传中, 2 上传成功,3 上传失败
      },

## methods 代码

handlePartUpload(option) {
      if (this.nullTxtFile) {
        this.fileVideoList = []
        return
      }
      this.updateLoading = true
      const file = option.file
      console.log(file, 'file')

      this.uploadProgress.uploadSize = 0
      this.uploadProgress.status = 0

      partUpload({
        file: file, // 视频实体
        pieceSize: 2, // 每个分片的大小 MB
	threadNumber: 2,  // 上传线程数量,不建议大于 5,最好不要大于文件的总片数
        init: data => { // 分片上传初始化 回调
          this.fileUploadData = data
        },
        success: data => { // 分片上传成功回调
          // option.onSuccess(data)
          this.fileUploadData = data
          // 获取后端生成的文件名
          this.$set(this.temp, 'video', data.fileName)
          this.fileVideoList.push({ name: data.fileName, url: data.fileUrl, status: 'success' })
          this.$message.success('上传成功')
          this.updateLoading = false
          console.log('分片上传视频成功')
        },
        error: e => {
          this.$message.error('上传失败,请重新上传')
          console.log('分片上传视频失败')
          this.updateLoading = false
        },
        progress: data => { // 上传进度回调
          this.uploadProgress = data
          option.onProgress({ percent: data.uploadSize / data.fileSize * 100 })// 更新进度条
          // 视频上传失败
          if (data.status == 3) {
            option.onError('上传失败')
            this.updateLoading = false
            // 视频上传成功
          } else if (data.status == 2) {
            this.updateLoading = false
          }
        }
      })
    },
removeVideo(file, fileList) {
      // 视频分片上传中
      if (this.uploadProgress.status == 1) {
        // 删除上传的分片
        fetchRemovePart(this.fileUploadData.fileName, this.fileUploadData.uploadId).then(res => {
        })
      }

      if (this.fileVideoList.length > 0 && this.fileVideoList[0].status == 'success') {
        // 视频上传完成
        if (this.uploadProgress.status == 2) {
          // 删除文件
          fetchRemoveFile(this.fileVideoList[0].name).then(res => {
          })
        }
      }

      this.updateLoading = false
      this.temp.video = ''
      this.fileVideoList = []
    },

/utils/partUpload.js 代码

记得先导包 npm install js-md5 --save

import md5 from 'js-md5'
import { uploadPartInit, uploadPartStart, uploadPartMerge } from '@/api/partUpload'

/**
 * 分片上传(多线程上传 使用Promise实现)
 * @param file      要上传的文件
 * @param pieceSize 每一片文件的大小 MB 为单位
 * @threadNumber    上传的线程数 最大不建议超过 5 (线程数请勿大于总片数 否则 都只有会有一个线程上传)
 * @param success   上传成功回调 返回 fileMD5: 文件的MD5值, fileName: 后端生成的文件名, fileUrl: 文件URL , uploadId:oss生成的 uploadId
 * @param error     上传失败回调
 * @param progress  文件分片上传进度回调 详见变量 progressData 上面的注释
 * @param init      初始化分片上传成功回调 返回 fileName: 后端生成的文件名, fileMD5: 文件的MD5, uploadId:oss生成的 uploadId
 */
export const partUpload = ({ file, pieceSize = 2, threadNumber = 2, success, error, progress, init }) => {
  /**
   * 上传的进度和状态
   * @fileSize 上传文件的总大小 单位:K
   * @uploadSize 已经上传文件的大小 单位:K
   * @status 上传的状态 1:上传中 , 2:上传成功 , 3:上传失败
   */
  const progressData = {
    fileSize: file.size,
    uploadSize: 0,
    status: 1
  }

  if (!file || !file.size) {
    progressData.status = 3
    progress && progress(progressData)
    error && error('无效的文件')
    return
  }

  // 上传过程中用到的变量
  let fileMD5 = ''// 总文件列表
  const partSize = pieceSize * 1024 * 1024 // 多少MB一片
  const partCount = Math.ceil(file.size / partSize) // 总片数
  let uploadId = '' // uploadId它是分片上传事件的唯一标识,可以根据这个ID来发起相关的操作,如取消分片上传、查询分片上传等。
  let fileName = '' // 文件名
  let successPartNumber = 0// 上传成功的分片数量
  // 合并分片时返回给后端的数据
  const partETagList = []

  const threadList = [] // 多线程上传开始和结束的分片位置
  for (let i = 0; i < threadNumber - 1; i++) {
    // 分片开始位置 和 分片结束位置
    threadList.push({ start: parseInt(partCount / threadNumber) * i, end: parseInt(partCount / threadNumber) * (i + 1) })
  }
  // 最后一个线程的分片开始位置 和 分片结束位置
  threadList.push({ start: threadList[threadList.length - 1].end ? threadList[threadList.length - 1].end : 0, end: partCount })

  // 获取md5
  const readFileMD5 = () => {
    progress && progress(progressData)
    // 读取文件的md5
    // console.log('获取文件的MD5值')
    const fileRederInstance = new FileReader()
    fileRederInstance.readAsBinaryString(file)
    fileRederInstance.addEventListener('load', e => {
      const fileBolb = e.target.result
      fileMD5 = md5(fileBolb)
      // 初始文件上传 获取 uploadId:
      uploadPartInit({ 'md5': fileMD5, 'fileName': file.name }).then(res => {
        // uploadId它是分片上传事件的唯一标识,可以根据这个ID来发起相关的操作,如取消分片上传、查询分片上传等。
        uploadId = res.value.uploadId
        // 后端生成的文件名
        fileName = res.value.fileName

        init && init({ fileName: fileName, fileMD5: fileMD5, uploadId: uploadId })

        // console.log(`文件开始上传 uploadId:${uploadId}, fileName: ${fileName}`)
        for (const thread of threadList) {
          readChunkMD5(thread.start, thread.end)
        }
      }).catch((e) => {
        progressData.status = 3
        progress && progress(progressData)
        error && error(e)
        // console.log('初始化分片上传失败')
      })
    })
  }
  const getChunkInfo = (file, currentChunk, chunkSize) => {
    const start = currentChunk * chunkSize
    const end = Math.min(file.size, start + chunkSize)
    const chunk = file.slice(start, end)
    return { start, end, part: chunk }
  }

  // 针对每个文件进行chunk处理
  async function readChunkMD5(start, end) {
    // 针对单个文件进行chunk上传
    for (let i = start; i < end; i++) {
      const { part } = getChunkInfo(file, i, partSize)
      // console.log('总片数' + partCount)
      // console.log('分片后的数据---测试:' + i)
      // console.log(part)
      // 阻塞 等待此分片上传完成再上传下一片
      await uploadChunk({ part: part, partNumber: i, partCount: partCount })
    }
  }

  const uploadChunk = (chunkInfo) => {
    return new Promise((resolve, reject) => {
      // progressFun()
      const fetchForm = new FormData()
      fetchForm.append('partCount', chunkInfo.partCount)
      fetchForm.append('partNumber', chunkInfo.partNumber + 1)
      fetchForm.append('partSize', chunkInfo.part.size)
      fetchForm.append('file', chunkInfo.part)
      fetchForm.append('md5', fileMD5)
      fetchForm.append('name', fileName)
      fetchForm.append('uploadId', uploadId)
      uploadPartStart(fetchForm, chunkInfo.partNumber + 1).then(res => {
        // 上传成功分片数量
        successPartNumber++
        // 文件上传进度
        progressData.uploadSize += chunkInfo.part.size
        progress && progress(progressData)

        // console.log('分片上传返回信息:' + res.value)
        partETagList.push(res.value.partETag)
        if (res.retcode == 0) {
          // success && success(res)
          // 当上传成功分片数 大于或等于 总分片数时
          if (successPartNumber >= chunkInfo.partCount) {
            // console.log('文件开始合并')
            uploadPartMerge({
              md5: fileMD5,
              name: fileName,
              uploadId: uploadId,
              partETagList: partETagList
            }).then(res => {
              if (res.retcode == 0) {
                progressData.status = 2
                progress && progress(progressData)
                // 上传成功回调
                success && success({
                  fileMD5: fileMD5,
                  fileName: fileName,
                  fileUrl: res.value.url,
                  uploadId: uploadId
                })
                // console.log('文件合并成功')
              } else {
                error && error('文件合并失败')
                // console.log(res.text)
              }
            }).catch((e) => {
              progressData.status = 3
              error && error(e)
              progress && progress(progressData)
              // console.log('文件合并错误')
            })
          }
        } else {
          error && error('上传失败')
          progressData.status = 3
          progress && progress(progressData)
          // console.log(res.text)
        }
        resolve()
      }).catch((e) => {
        throw new Error('上传失败, 文件被移除')
      })
    })
  }
  readFileMD5() // 开始执行代码
}

/api/partUpload.js 代码

import request from '@/utils/request'

export function uploadPartInit(data) {
  return request({
    url: 'upload/part/init',
    method: 'post',
    params: data
  })
}

export function uploadPartStart(data, partNumber) {
  return request({
    url: 'upload/part/' + partNumber,
    method: 'post',
    data
  })
}

export function uploadPartMerge(data) {
  return request({
    url: 'upload/part/merge',
    method: 'post',
    params: {
      'uploadId': data.uploadId,
      'fileName': data.name
    },
    data: data.partETagList
  })
}

export function fetchRemoveFile(fileName) {
  return request({
    url: '/upload/delete/file',
    method: 'post',
    params: {
      fileName: fileName
    }
  })
}

export function fetchRemovePart(fileName, uploadId) {
  return request({
    url: '/upload/delete/part',
    method: 'post',
    params: {
      fileName: fileName,
      uploadId: uploadId
    }
  })
}

前端分片上传部分代码参考(没错ctrl C V工程师就是在下了) 点击前往

Q.E.D.