小工具知识网

当前位置: 首页 > 行业知识 > Java实现分片上传(前端分,后端合并)

行业知识

Java实现分片上传(前端分,后端合并)

2026-11-15 08:03:57 来源:互联网转载

注:一些实体类数据自行修改 

1,api文件 

import request from '@/router/axiosInfo';export const uploadChunk = (data) => {  return request({    url: '/api/blade-sample/sample/covid19/uploadChunk',    method: 'post',    data  })}export const uploadMerge = (data) => {  return request({    url: '/api/blade-sample/sample/covid19/uploadMerge',    method: 'post',    data  })}

2, axios文件

/** * 全站http配置 * * axios参数说明 * isSerialize是否开启form表单提交 * isToken是否需要token */import axios from 'axios';import store from '@/store/';import router from '@/router/router';import { serialize } from '@/util/util';import { getToken } from '@/util/auth';import { Message } from 'element-ui';import { isURL } from "@/util/validate";import website from '@/config/website';import { Base64 } from 'js-base64';import { baseUrl } from '@/config/env';// import NProgress from 'nprogress';// import 'nprogress/nprogress.css';import crypto from '@/util/crypto';//默认超时时间axios.defaults.timeout = 100000;//返回其他状态码axios.defaults.validateStatus = function (status) {  return status >= 200 && status  {  //开启 progress bar  // NProgress.start();  //地址为已经配置状态则不添加前缀  if (!isURL(config.url) && !config.url.startsWith(baseUrl)) {    config.url = baseUrl + config.url  }  //headers判断是否需要  const authorization = config.authorization === false;  if (!authorization) {    config.headers['Authorization'] = `Basic ${Base64.encode(`${website.clientId}:${website.clientSecret}`)}`;  }  //headers判断请求是否携带token  const meta = (config.meta || {});  const isToken = meta.isToken === false;  //headers传递token是否加密  const cryptoToken = config.cryptoToken === true;  //判断传递数据是否加密  const cryptoData = config.cryptoData === true;  const token = getToken();  if (token && !isToken) {    config.headers[website.tokenHeader] = cryptoToken      ? 'crypto ' + crypto.encryptAES(token, crypto.cryptoKey)      : 'bearer ' + token;  }  // 开启报文加密  if (cryptoData) {    if (config.params) {      const data = crypto.encryptAES(JSON.stringify(config.params), crypto.aesKey);      config.params = { data };    }    if (config.data) {      config.text = true;      config.data = crypto.encryptAES(JSON.stringify(config.data), crypto.aesKey);    }  }  //headers中配置text请求  if (config.text === true) {    config.headers["Content-Type"] = "text/plain";  }  //headers中配置serialize为true开启序列化  if (config.method === 'post' && meta.isSerialize === true) {    config.data = serialize(config.data);  }  return config}, error => {  return Promise.reject(error)});//http response 拦截axios.interceptors.response.use(res => {  //关闭 progress bar  // NProgress.done();  //获取状态码  const status = res.data.code || res.status;  const statusWhiteList = website.statusWhiteList || [];  const message = res.data.msg || res.data.error_description || '未知错误';  const config = res.config;  const cryptoData = config.cryptoData === true;  //如果在白名单里则自行catch逻辑处理  if (statusWhiteList.includes(status)) return Promise.reject(res);  //如果是401则跳转到登录页面  if (status === 401) store.dispatch('FedLogOut').then(() => router.push({ path: '/login' }));  // 如果请求为非200否者默认统一处理  if (status !== 200) {    Message({      message: message,      type: 'error'    });    return Promise.reject(new Error(message))  }  // 解析加密报文  if (cryptoData) {    res.data = JSON.parse(crypto.decryptAES(res.data, crypto.aesKey));  }  return res;}, error => {  // NProgress.done();  return Promise.reject(new Error(error));});export default axios;

3,vue源码 

      选取文件    上传到服务器      import { uploadMerge, uploadChunk } from "@/api/sample/sampleCovid19Info";export default {  data() {    return {      percentage: 0,      customColor: '#409eff',      fileList: [], // 文件列表        chunks: [], // 切片数组        currentChunkIndex: 0, // 当前切片索引        totalChunks: 0, // 总切片数        currentFile: null, // 当前处理文件        totalSize: 0,      loading: false    };  },  methods: {    async uploadMerge() {      this.loading = true      const formData = new FormData();      formData.append('fileName', this.currentFile.name);      formData.append('totalSize', this.totalSize);      console.log(22222);      try {        let res = await uploadMerge({ fileName: this.currentFile.name, totalSize: this.totalSize })        console.log(res, '2');        if (res.data.code === 200) {          this.fileList = []          this.chunks = []          this.currentChunkIndex = 0          this.totalChunks = 0          this.currentFile = null          this.totalSize = 0          this.$message({            type: "success",            message: "添加成功!"          });          this.loading = false        } else {          this.$message({            type: "success",            message: "添加失败!"          });          this.loading = false        }      } catch (error) {        this.$message.error(error)      } finally {        this.loading = false      }    },    handleChange(file, fileList) {      this.totalSize = file.size      // 当文件变化时,执行切片操作        if (file.status === 'ready') {        this.currentFile = file.raw; // 获取原始文件对象          this.sliceFile(this.currentFile);      }    },    async sliceFile(file) {      const chunkSize = 1024 * 1024 * 10; // 切片大小设置为1MB        const totalSize = file.size;      this.totalChunks = Math.ceil(totalSize / chunkSize);      for (let i = 0; i  

4,切片请求接口

/** * 大上传文件 */@PostMapping("/uploadChunk")@ApiOperationSupport(order = 15)@ApiOperation(value = "大上传文件", notes = "大上传文件")public R uploadChunk(@RequestParam("chunk") MultipartFile chunk,                @RequestParam("chunkIndex") Integer chunkIndex,                @RequestParam("totalChunks") Integer totalChunks,                @RequestParam("fileName") String fileName,                @RequestParam("totalSize") Long totalSize) throws IOException {   Boolean isOk = sampleCovid19Service.LargeFiles(chunk, chunkIndex, totalChunks, fileName, totalSize);   return R.status(isOk);}
/** * 大上传文件 * @param chunk 文件流 * @param chunkIndex 切片索引 * @param totalChunks 切片总数 * @param fileName 文件名称 * @param totalSize 文件大小 * @return */Boolean LargeFiles(MultipartFile chunk, Integer chunkIndex, Integer totalChunks, String fileName, Long totalSize);

 

/** * 大上传文件 * @param chunk 文件流 * @param chunkIndex 切片索引 * @param totalChunks 切片总数 * @param fileName 文件名称 * @param totalSize 文件大小 * @return */@Overridepublic Boolean LargeFiles(MultipartFile chunk, Integer chunkIndex, Integer totalChunks, String fileName, Long totalSize) {   if (chunk == null) {      throw new ServiceException("添加失败,文件名称获取失败");   }   if (chunkIndex == null) {      throw new ServiceException("添加失败,分片序号不能为空");   }   if (fileName == null) {      throw new ServiceException("添加失败,文件名称获取失败");   }   if (totalSize == null || totalSize == 0) {      throw new ServiceException("添加失败,文件名大小不能为空或0");   }   //获取文件名称   fileName = fileName.substring(0, fileName.lastIndexOf("."));   File chunkFolder = new File(chunkPath + totalSize + "/" + fileName);   //判断文件路径是否创建   if (!chunkFolder.exists()) {      chunkFolder.mkdirs();   }   //直接将文件写入   File file = new File(chunkPath + totalSize + "/" + fileName + "/" + chunkIndex);   try {      chunk.transferTo(file);   } catch (IOException e) {      log.info("时间:{}" + new Date() + "文件名称:{}" + fileName + "上传失败");      throw new ServiceException("时间:{}" + new Date() + "文件名称:{}" + fileName + "上传失败");   }   return true;}

 5,合并接口

package org.springblade.sample.vo;import lombok.Data;@Datapublic class UploadMergeVO {   //文件上传名称   private String fileName;   //文件大小   private Long totalSize;}
/** * 大文件合并 */@PostMapping("/uploadMerge")@ApiOperationSupport(order = 16)@ApiOperation(value = "文件合并", notes = "文件合并")public R uploadMerge(@RequestBody UploadMergeVO vo) throws IOException {   Boolean isOk = sampleCovid19Service.LargeMerge(vo);   return R.status(isOk);}
/** *  大文件合并 * @param vo  * @return * @throws IOException */Boolean LargeMerge(UploadMergeVO vo) throws IOException;

 

/** *  大文件合并 * @param vo * @return * @throws IOException */@Overridepublic Boolean LargeMerge(UploadMergeVO vo) throws IOException {   if (StringUtil.isBlank(vo.getFileName())) {      throw new ServiceException("上传失败,文件名称获取失败");   }   if (vo.getTotalSize() == 0) {      throw new ServiceException("上传失败,文件大小不能为空");   }   //块文件目录   String fileNameInfo = vo.getFileName().substring(0, vo.getFileName().lastIndexOf("."));   //合并文件夹   File chunkFolder = new File(chunkPath + vo.getTotalSize() + "/" + fileNameInfo + "/");   File depositFile = new File(sourceFile + vo.getTotalSize() + "/" + fileNameInfo + "/");   //创建文件夹   if (!depositFile.exists()) {      depositFile.mkdirs();   }   //创建新的合并文件   File largeFile = new File(depositFile + "/" + vo.getFileName());   largeFile.createNewFile();   //用于写文件   RandomAccessFile raf_write = new RandomAccessFile(largeFile, "rw");   //指针指向文件顶端   raf_write.seek(0);   //缓冲区   byte[] b = new byte[1024];   //分块列表   File[] fileArray = chunkFolder.listFiles();   // 转成集合,便于排序   List fileList = Arrays.asList(fileArray);   // 从小到大排序   Collections.sort(fileList, new Comparator() {      @Override      public int compare(File o1, File o2) {         return Integer.parseInt(o1.getName()) - Integer.parseInt(o2.getName());      }   });   //合并文件   for (File chunkFile : fileList) {      RandomAccessFile raf_read = new RandomAccessFile(chunkFile, "rw");      int len = -1;      while ((len = raf_read.read(b)) != -1) {         raf_write.write(b, 0, len);      }      raf_read.close();   }   raf_write.close();   //取出合并文件大小和hash   long fileSize = getFileSize(largeFile);   int hashCode = largeFile.hashCode();   //判断是否合并成功   if (fileSize == vo.getTotalSize()) {      //将内容写入数据库      FileBack fileBack = new FileBack();      fileBack.setFileName(vo.getFileName());      fileBack.setFileSize(Integer.parseInt(String.valueOf(vo.getTotalSize())));      fileBack.setUpdateTime(new Date());      fileBack.setFileUrl(chunkFolder.toString());      fileBack.setFileHash(String.valueOf(hashCode));      fileBack.setUploadData(new Date());      fileBackService.save(fileBack);      //删除临时文件目录      File chunkFolderInfo = new File(chunkPath + vo.getTotalSize() + "/");      FileUtils.deleteDirectory(chunkFolderInfo);      log.info("时间:{}" + new Date() + "文件名称:{}" + vo.getFileName() + "上传成功");      return true;   } else {      //删除临时文件目录      File chunkFolderInfo = new File(chunkPath + vo.getTotalSize() + "/");      FileUtils.deleteDirectory(chunkFolderInfo);      log.info("时间:{}" + new Date() + "文件名称:{}" + vo.getFileName() + "上传失败");      return false;   }}

上一篇:【Spring】三级缓存

下一篇:C#.net8创建webapi,使用SqlSugar,仓储模式,DTO,服务层,控制层的综合应用(企业级)