1. 项目背景
由于用户需求,需要上传大量图片,只能通过上传压缩包的形式上传,可是压缩包过大时,又会出现上传超时的情况,故需要将压缩包分块上传,然后解压缩图片、若图片过大则再对图片进行压缩。

2. 分块上传
分块上传我在用的时候发现有两种:第一种:分块合并接口全由后端接口生成;第二种:前端分块,后端上传
开始用的第一种,结果发现生成的文件没有内容,应该是向文件内写的方法有问题;所以又用的第二种;

2.1. 前端分页
js文件:

import http from './index'
export const white = (data) => http({
    url: '/api/file/upload',
    method: 'post',
    data
})



页面:

<template>
    <div>
      <el-form ref="editForm" :model="formFileds">
        <el-row :gutter="24">

          <el-col :span="12">
            <el-form-item>
              <el-input></el-input>
            </el-form-item>
          </el-col>

          <el-col :span="12">
            <el-form-item>
              <el-button type="primary">
                查询
              </el-button>

              <el-button type="primary" @click="add()">
                增加
              </el-button>

            </el-form-item>
          </el-col>

        </el-row>


      </el-form>
      <div>
            <!-- <button type="button" v-on:click="selectFile()" class="btn btn-white btn-default btn-round">
                <i class="ace-icon fa fa-upload"></i>{{ text }}
            </button> -->
            <input class="hidden" type="file" ref="file" v-on:change="uploadFile()" v-bind:id="inputId + '-input'">        </div>
      <!--表格内容-->
      <el-table ref="list" :data="tableData" style="width: 100%;margin-bottom: 5=1px;" border stripe highlight-current-row
        :default-sort="{ prop: 'sx', order: 'sx' }">
        <!-- v-for="item in tableData" v-bind:key="item.index" -->
        <el-table-column fixed type="selection" width="45">
        </el-table-column>
        <el-table-column property="name" label="名称" align="center">
        </el-table-column>
        <!-- <el-form-item label="发布状态" prop="fbzt">
          <el-switch ></el-switch>
        </el-form-item> -->
        <el-table-column property="fbzt" label="发布状态" align="center">
          <template slot-scope="scope">
            <el-switch v-model="scope.row.fbzt" disabled></el-switch>
          </template>
        </el-table-column>
        <el-table-column property="sx" label="顺序" align="center">
        </el-table-column>
        <el-table-column fixed="right" label="操作" align="center">
          <template slot-scope="scope">
            <!-- <el-button circle icon="el-icon-refresh" title="重置密码" type="success" size="small"></el-button>
            <el-button circle icon="el-icon-phone" title="变更手机号" type="success" size="small"></el-button> -->
            <el-button circle icon="el-icon-edit-outline" type="primary" @click="Compile()" title="编辑" size="small">
            </el-button>
            <el-button circle icon="el-icon-delete" type="danger" title="删除" size="small"
              @click="rowDel(scope.row.id)"></el-button>
          </template>
        </el-table-column>
      </el-table>
      <!--分页-->
      <el-pagination :page-sizes="[10, 20, 30, 40, 100]" :page-size="10" :total="100"
        layout="total, sizes, prev, pager, next, jumper">
      </el-pagination>

      <Add v-if="isShowAddDialog" :isShowAddDialog="isShowAddDialog" @dialogClose="dialogClose"></Add>
      <Compile v-if="isShowComDialog" :isShowComDialog="isShowComDialog" @dialogClose="dialogClose"></Compile>
    </div>
  </template>

  <script>
  import Add from "./auctionProcessAdd.vue";
  import Compile from "./auctionProcesscomple.vue";
  import { getAllJmxz,deleteJmxz } from '@/api/systemAdministrator/auctionProcess';
  import {white} from '@/api/white'
  import axios from "axios";

  export default {
    name: "auctionProcess",
    components: {
      Add,
      Compile,
    },
    data() {
      return {
        editProps: '',
            text: '',
            inputId: '',
            hex_md5: '',
        formFileds: {
        },
        tableData: [
        {
        id: 0,
        name: '',
        fbzt: '',
        sx: 0,
        nr: "",
        bz: '',
        },
        ],
        pageSize: 10,
        total: 0,
        currentPage: 1,
        editProps: '',
        isShowAddDialog:false,
        isShowComDialog:false,      }
    },
    methods: {
      /**
       * 点击【上传】
       */
        // selectFile () {
        //     let _this = this;
        //     $("#" + _this.inputId + "-input").trigger("click");
        // },        /**
      * 上传文件
      */
      uploadFile () {
            let _this = this;
            // 1. 获取 input 中被选中的文件
            let file = _this.$refs.file.files[0];            // 2. 生成文件标识,标识多次上传的是不是同一个文件
            //  let key = md5(file.name + file.size + file.type);
            // let key10 = parseInt(key, 16);
            // let key62 = Tool._10to62(key10);
            let time = Date.now();
            // console.log(time);
            let fileName = file.name;
            let temp = fileName.split('.');
            let key62 = time + '`' + temp[0];
            // console.log(key62);
            // 判断文件格式 (非必选,根据实际情况选择是否需要限制文件上传类型)
            // let suffixs = _this.suffixs;s

            let suffix = fileName.substring(fileName.lastIndexOf(".") + 1, fileName.length).toLowerCase();
            // if (!(!suffixs || JSON.stringify(suffixs) === "{}" || suffixs.length === 0)) {
            //     let validateSuffix = false;
            //     for (let s of suffixs) {
            //         if (s.toLocaleLowerCase() === suffix) {
            //             validateSuffix = true;
            //             break;
            //         }
            //     }
            //     if (!validateSuffix) {
            //         Toast.warning("文件格式不正确!只支持上传:" + suffixs.join(","));
            //         $("#" + _this0.inputId + "-input").val("");
            //         return;
            //     }
            // }            // 3. 文件分片开始
            // 3.1 设置与计算分片必选参数
            let shardSize = 20 * 1024 * 1024; // 20M为一个分片
            let shardIndex = 1;   // 分片索引,1表示第1个分片
            let size = file.size; // 文件的总大小
            let shardTotal = Math.ceil(size / shardSize); // 总分片数            //  3.2 拼接将要传递到参数, use 非必选,这里用来标识文件用途。
            let param = {
                'shardIndex': shardIndex,
                'shardSize': shardSize,
                'shardTotal': shardTotal,
                'use': _this.use,
                'name': file.name,
                'suffix': suffix,
                'size': file.size,
                'key': key62
            };            //  3.3  传递分片参数,通过递归完成分片上传。
            _this.upload(param);        },
        /**
   * 递归上传分片
   */
        upload (param) {
            let _this = this;
            let shardIndex = param.shardIndex;
            let shardTotal = param.shardTotal;
            let shardSize = param.shardSize;
            // 3.3.1 根据参数,获取文件分片
            let fileShard = _this.getFileShard(shardIndex, shardSize);            // 3.3.2 将文件分片转为base64进行传输
            let fileReader = new FileReader();
            // 读取并转化 fileShard 为 base64
            fileReader.readAsDataURL(fileShard);
            //  readAsDataURL 读取后的回调,
            // 将 经过  base64 编码的  分片 整合到 param ,发送给后端,从而上传分片。
            fileReader.onload = function (e) {
                let base64 = e.target.result;
                param.shard = base64;
                console.log('shard',param.shard)
                //   Loading.show();
                let params = new FormData();
                params = param;
                // params.append=(param);
                // const params2 = JSON.stringify(params);
                // console.log('params2',params2);
                white(params).then((res) => {
                  console.log('!!!!!!!!!!!!!!!!!!!!!!!!!');
                  // console.log(params);
                // axios.post('/api/file/upload', params).then((res)=> {
                    //   Loading.hide();
                    let resp = res.data;
                    // 上传结果
                    // 当前分片索引小于 分片总数,继续执行分派,反之 则表示全部上传成功。
                    if (shardIndex < shardTotal) {
                        // 上传下一个分片
                        param.shardIndex = param.shardIndex + 1;
                        // this.$message.warning('正在上传');
                        _this.upload(param);
                    } else {
                        // 文件上传成功后的回调
                        // _this.afterUpload(resp);
                        return ;
                        // this.$message.success('上传成功');
                    }
                    // $('#' + _this.inputId + '-input').val('');
                });
            };
        },
        // axios.post('/api/file/upload',params).then((res)=>{
        // // white(param).then((res) => {
        // //   _this.$ajax.post( "localhost:8080/admin/upload", param).then((res)=> {
        //     //   Loading.hide();s
        //       let resp = res.data;
        //       // 上传结果
        //         // 当前分片索引小于 分片总数,继续执行分派,反之 则表示全部上传成功。
        //       if(shardIndex < shardTotal) {
        //         // 上传下一个分片
        //         param.shardIndex = param.shardIndex + 1;
        //         _this.upload(param);
        //       } else {
        //           // 文件上传成功后的回调
        //          _this.afterUpload(resp);
        //       }
        //       $("#" + _this.inputId + "-input").val("");
        //   });        /**
        * 文件分片函数
        */
        getFileShard (shardIndex, shardSize) {
            let _this = this;
            let file = _this.$refs.file.files[0];
            let start = (shardIndex - 1) * shardSize; // 当前分片起始位置
            let end = Math.min(file.size, start + shardSize); // 当前分片结束位置
            let fileShard = file.slice(start, end); // 从文件中截取当前的分片数据
            return fileShard;
        },
      rowEdit(row) {
        if(row.fbzt == '已提交'){
          row.fbzt = 'true';
        }
        else{
          row.fbzt = 'false';
        }

        // //当前选中行
        this.rowDataS = row;
        console.log(1)
      },
      dialogClose() {
        this.isShowEditDialog = false;
        this.isShowAddDialog = false;
        this.isShowComDialog = false;
      },
      handleEdit(id) {
        this.$refs.editForm.validate(isValid => {

          if (!isValid) return;

          // 保存编辑后的数据
          Object.assign(this.tableData[id], this.formFileds);
          this.isShowEditDialog = false;

          // 考虑到可能编辑了日期-需要重新排序
          // ***注意:手动排序传参和表格的default-sort属性格式不太一样
          this.$refs.list.sort('date', 'descending');

          this.$message.success('编辑成功');
        });
      },
      getList(){
            this.param = 'pageno=' + this.currentPage + '&countonepage=' + this.pageSize;
            getAllJmxz(this.param).then((res) => {
                if(res){
                    console.log(res.data);
                    console.log(res.msg);
                    this.tableData = res.data.list;
                    this.total = res.data.total;
                    for(let item of this.tableData){
                      if(item.fbzt == '已提交'){
                        item.fbzt = true;
                      }
                      else{
                        item.fbzt = false;
                      }
                    }
                }
            })
        },
      add() {
        console.log("add");
        this.isShowAddDialog = true;
      },
      Compile() {
        console.log("Compile");
        this.isShowComDialog = true;
      },

      rowDel(id) {
        // this.formFileds = row;
        console.log(id);
            this.$confirm('确定要删除当前行吗?', '删除', {
                comfirmButtonText: '确定',
                cancelButtonText: '取消'
            }).then(() => {
                deleteJmxz(id).then((res) => {
                    if(res){
                        console.log(res.data);
                        console.log(res.msg);
                        this.tableData.splice(id, 1);
                        this.$message.success('删除成功');
                        this.getList();
                    }
                })
            });
      },
      // 选中当前行-当前行的复选框被勾选
      setCurRowChecked(row) {

        this.$refs.list.clearSelection();
        this.$refs.list.toggleRowSelection(row);
      },
    },
    mounted(){
        this.getList();
        // console.log(this.tableData);
        // console.log(new Date().toLocaleString());
    }
  }
  </script>

  <style scoped lang="less">
  .el-form {
    padding: 0 10px;
  }

  .el-date-editor {
    width: 100% !important;
  }
  </style>

2.2. 后端合并
接收类:

package com.example.springboot.domain;
import lombok.Data;
import com.fasterxml.jackson.annotation.JsonFormat;
import java.util.Date;
@Data
public class FileDto {    /**
     * id
     */
    private String id;    /**
     * 相对路径
     */
    private String path;    /**
     * 文件名
     */
    private String name;    /**
     * 后缀
     */
    private String suffix;    /**
     * 大小|字节B
     */
    private Integer size;    /**
     * 用途
     */
    private String use;    /**
     * 创建时间
     */
    @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8")
    private Date createdAt;    /**
     * 修改时间
     */
    @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8")
    private Date updatedAt;    private Integer shardIndex;
    private Integer shardSize;
    private Integer shardTotal;
    private String key;
    /**
     * base64
     */
    private String shard;
}Controller:
package com.example.springboot.controller;
 
import cn.hutool.core.img.ImgUtil;
import cn.hutool.core.io.FileUtil;
import cn.hutool.core.util.ZipUtil;
import com.example.springboot.common.Base64ToMultipartFile;
import com.example.springboot.common.FileViewer;
import com.example.springboot.common.ReturnResult;
import com.example.springboot.domain.FileDto;
import net.coobird.thumbnailator.Thumbnails;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.multipart.MultipartFile;import java.io.*;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;@RequestMapping("/file")
@RestController
public class UploadController {    private static final Logger LOG = LoggerFactory.getLogger(UploadController.class);
    private static final String storePath = "E:/usr/local/apps/pmxt-download/merge/";
    @PostMapping("/upload")
    public ReturnResult upload(@RequestBody FileDto fileDto) throws Exception {
        Date date = new Date();
        SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyyy/MM/dd/");
        String format = simpleDateFormat.format(date);
        LOG.info("日期信息:"+format);
        String tem = storePath + format + fileDto.getName();
        File f1 = new File(tem);
        if (f1.exists()){
            f1.delete();
        }
        LOG.info("上传文件开始");
        String use = fileDto.getUse();
        String key = fileDto.getKey();
        String suffix = fileDto.getSuffix();
        String shardBase64 = fileDto.getShard();
        MultipartFile shard = Base64ToMultipartFile.base64ToMultipart(shardBase64);        // 保存文件到本地
        String fullPath = storePath + format;
        if (!new File(fullPath).exists()){
            new File(fullPath).mkdirs();
        }
        String path = new StringBuffer(fullPath)
                .append(key.split("`")[1])
                .append(".")
                .append(suffix)
                .toString(); // course\6sfSqfOwzmik4A4icMYuUe.mp4
        System.out.println(path);
        String localPath = new StringBuffer(path)
                .append(".")
                .append(fileDto.getShardIndex())
                .toString(); // course\6sfSqfOwzmik4A4icMYuUe.mp4.1
        System.out.println(localPath);
        File dest = new File(localPath);
        shard.transferTo(dest);
        System.out.println(dest.getAbsolutePath());
        LOG.info("保存文件记录开始");
        fileDto.setPath(path);
        // 合并分片
        if (fileDto.getShardIndex().equals(fileDto.getShardTotal())) {
            this.merge(fileDto);
        }
//        ResponseDto<Object> responseDto = new ResponseDto<>();
//        responseDto.setContent("http://127.0.0.1:8080/f/"+ format + key + "-" + fileName);
//        return responseDto;
        return ReturnResult.buildSuccessResult("true");
    }    public void merge(FileDto fileDto) throws Exception {
        LOG.info("合并分片开始");
        String path = fileDto.getPath(); //http://127.0.0.1:9000/file/f/course\6sfSqfOwzmik4A4icMYuUe.mp4
        Integer shardTotal = fileDto.getShardTotal();
        File newFile = new File(path);
        FileOutputStream outputStream = new FileOutputStream(newFile, true);//文件追加写入
        FileInputStream fileInputStream = null;//分片文件
        byte[] byt = new byte[10 * 1024 * 1024];
        int len;
        try {
            for (int i = 0; i < shardTotal; i++) {
                // 读取第i个分片
                fileInputStream = new FileInputStream(new File( path + "." + (i + 1))); //  course\6sfSqfOwzmik4A4icMYuUe.mp4.1
                while ((len = fileInputStream.read(byt)) != -1) {
                    outputStream.write(byt, 0, len);
                }
            }
        } catch (IOException e) {
            LOG.error("分片合并异常", e);
        } finally {
            try {
                if (fileInputStream != null) {
                    fileInputStream.close();
                }
                outputStream.close();
                LOG.info("IO流关闭");
            } catch (Exception e) {
                LOG.error("IO流关闭", e);
            }
        }
        LOG.info("合并分片结束");        // 删除分片
        LOG.info("删除分片开始");
        File file = null;
        String filePath = "";
        for (int i = 0; i < shardTotal; i++) {
            filePath =  path + "." + (i + 1);
            file = new File(filePath);
            System.gc();
            boolean result = file.delete();
            LOG.info("删除{},{}", filePath, result ? "成功" : "失败");
        }
        LOG.info("删除分片结束");
    }    @PostMapping("/unzip")
    public ReturnResult unzip(String path){
        return ReturnResult.buildSuccessResult(ZipUtil.unzip(path, "E:/usr/local/apps/pmxt-download/merge"));
    }    @PostMapping("/selectBySize")
    public ReturnResult selectBySize(String path) {
        List<String> flist = FileViewer.getListFiles(path,true);
        for (String s : flist) {
            File file = new File(s);
            if (file.isDirectory()) {
                return ReturnResult.buildFailureResult("文件不存在");
            } else {
                if(file.length()>1048576*3){
                    ImgUtil.scale(
                            FileUtil.file(s),
                            FileUtil.file(s),
                            0.5f//缩放比例
                    );
//                    try {
//                        Thumbnails.of(s)
//                                .scale(0.5f)
//                                .outputQuality(0.5f)
//                                .toFile(s);
//                    } catch (IOException e) {
//                        throw new RuntimeException(e);
//                    }
                }
            }
        }
        return ReturnResult.buildSuccessResult(true);    }
}

工具类:64位编码转文件

package com.example.springboot.common;
import org.springframework.web.multipart.MultipartFile;
import java.io.*;
import java.util.Base64;public class Base64ToMultipartFile implements MultipartFile {
    private final byte[] imgContent;
    private final String header;    public Base64ToMultipartFile(byte[] imgContent, String header) {
        this.imgContent = imgContent;
        this.header = header.split(";")[0];
    }    @Override
    public String getName() {
        // TODO - implementation depends on your requirements
        return System.currentTimeMillis() + Math.random() + "." + header.split("/")[1];
    }    @Override
    public String getOriginalFilename() {
        // TODO - implementation depends on your requirements
        return System.currentTimeMillis() + (int) Math.random() * 10000 + "." + header.split("/")[1];
    }    @Override
    public String getContentType() {
        // TODO - implementation depends on your requirements
        return header.split(":")[1];
    }    @Override
    public boolean isEmpty() {
        return imgContent == null || imgContent.length == 0;
    }    @Override
    public long getSize() {
        return imgContent.length;
    }    @Override
    public byte[] getBytes() throws IOException {
        return imgContent;
    }    @Override
    public InputStream getInputStream() throws IOException {
        return new ByteArrayInputStream(imgContent);
    }    @Override
    public void transferTo(File dest) throws IOException, IllegalStateException {
        new FileOutputStream(dest).write(imgContent);
    }    public static MultipartFile base64ToMultipart(String base64) {
        System.out.println(base64);
        String[] baseStrs = base64.split(",");
//            Encoder decoder = Base64.getEncoder();
        Base64.Decoder decoder = Base64.getDecoder();
        byte[] b;
        b = decoder.decode(baseStrs[1]);        for(int i = 0; i < b.length; ++i) {
            if (b[i] < 0) {
                b[i] += 256;
            }
        }        return new Base64ToMultipartFile(b, baseStrs[0]);
    }
}

返回类(若返回为字符串或者已有返回类,则这个类没用):

package com.example.springboot.common;
import lombok.Data;
@Data
public class ReturnResult {
    // 状态码
    private int code;
    // 消息
    private String msg;
    // 数据
    private Object data;    private ReturnResult(){
    }
    private static ReturnResult buildResult(int code, String msg, Object data) {
        ReturnResult result = new ReturnResult();
        result.code = code;
        result.msg = msg;
        result.data = data;
        return result;
    }    public static ReturnResult buildSuccessResult(String msg, Object data) {
        return buildResult(200, msg, data);
    }    public static ReturnResult buildSuccessResult(Object data) {
        return buildSuccessResult("success", data);
    }    public static ReturnResult buildFailureResult(int code, String msg, Object data) {
        return buildResult(code, msg, data);
    }    public static ReturnResult buildFailureResult(String msg, Object data) {
        return buildFailureResult(500, msg, data);
    }    public static ReturnResult buildFailureResult(String msg) {
        return buildFailureResult(500, msg, null);
    }
}

参考:https://windcoder.com/dawenjianfenpianshangchuanjavabanjiandanshixian

3. 压缩与解压缩

@PostMapping("/unzip")
    public ReturnResult unzip(String path){
        return ReturnResult.buildSuccessResult(ZipUtil.unzip(path, "E:/usr/local/apps/pmxt-download/merge"));
    }



参考:http://hutool.cn/docs/index.html#/core/工具类/压缩工具-ZipUtil

4. 图片压缩
这里我的实现主要有两种:Google图片处理工具与hutool工具,我都用了一下,发现前者将3700kb质量比例双压缩后为1295kb,后者仅是比例压缩就将3700kb压缩为了1100kb,但是前者对图片的处理方式更多,所以用那种,根据自身需求选择

文件夹处理工具类:

package com.example.springboot.common;
import java.io.File;
import java.text.DecimalFormat;
import java.util.ArrayList;
import java.util.List;public class FileViewer {
    public static List<String> getListFiles(String path, boolean isdepth) {
        List<String> lstFileNames = new ArrayList<String>();
        File file = new File(path);
        return FileViewer.listFile(lstFileNames, file, "jpg", isdepth);
    }    private static List<String> listFile(List<String> lstFileNames, File f,
                                         String suffix, boolean isdepth) {
        // 若是目录, 采用递归的方法遍历子目录
        if (f.isDirectory()) {
            File[] t = f.listFiles();
            for (int i = 0; i < t.length; i++) {
                if (isdepth || t[i].isFile()) {
                    listFile(lstFileNames, t[i], suffix, isdepth);
                }
            }
        } else {
            String filePath = f.getAbsolutePath();
                if (!suffix.equals("")) {
                    int begIndex = filePath.lastIndexOf("."); // 最后一个.(即后缀名前面的.)的索引
                    String tempsuffix = "";
                    if (begIndex != -1) {
                        tempsuffix = filePath.substring(begIndex + 1,
                                filePath.length());
                        if (tempsuffix.equals(suffix) ||  tempsuffix.equals("bmp") || tempsuffix.equals("jpeg") || tempsuffix.equals("png") || tempsuffix.equals("gif")) {
                            lstFileNames.add(filePath);
                        }
                    }
                } else {
                    lstFileNames.add(filePath);
                }        }
        return lstFileNames;
    }    // 递归取得文件夹(包括子目录)中所有文件的大小
    public static long getFileSize(File f) throws Exception// 取得文件夹大小
    {
        long size = 0;
        File flist[] = f.listFiles();
        for (int i = 0; i < flist.length; i++) {
            if (flist[i].isDirectory()) {
                size = size + getFileSize(flist[i]);
            } else {
                size = size + flist[i].length();
            }
        }
        return size;
    }    public static String FormetFileSize(long fileS) {// 转换文件大小
        DecimalFormat df = new DecimalFormat("#.00");
        String fileSizeString = "";
        if (fileS < 1024) {
            fileSizeString = df.format((double) fileS) + "B";
        } else if (fileS < 1048576) {
            fileSizeString = df.format((double) fileS / 1024) + "K";
        } else if (fileS < 1073741824) {
            fileSizeString = df.format((double) fileS / 1048576) + "M";
        } else {
            fileSizeString = df.format((double) fileS / 1073741824) + "G";
        }
        return fileSizeString;
    }}

遍历文件夹下所有图片,过大则进行压缩:

@PostMapping("/selectBySize")
    public ReturnResult selectBySize(String path) {
        List<String> flist = FileViewer.getListFiles(path,true);
        for (String s : flist) {
            File file = new File(s);
            if (file.isDirectory()) {
                return ReturnResult.buildFailureResult("文件不存在");
            } else {
                if(file.length()>1048576*3){
                    ImgUtil.scale(
                            FileUtil.file(s),
                            FileUtil.file(s),
                            0.5f//缩放比例
                    );
//                    try {
//                        Thumbnails.of(s)
//                                .scale(0.5f)
//                                .outputQuality(0.5f)
//                                .toFile(s);
//                    } catch (IOException e) {
//                        throw new RuntimeException(e);
//                    }
                }
            }
        }
        return ReturnResult.buildSuccessResult(true);    }

 

参考文章:http://blog.ncmem.com/wordpress/2023/12/11/springbootvue实现大文件分块上传/