java分片上传zip_利用SpringBoot和vue-simple-uploader进行文件的分片上传

该博客介绍了如何利用SpringBoot后端和vue-simple-uploader前端库,实现Java的分片上传zip文件功能。通过ServletFileUpload检查表单格式,MultipartFileParam接收文件信息,ChunkService处理分片写入和文件校验,最后完成文件的合并与重命名。
摘要由CSDN通过智能技术生成

引入工具 commons-io

commons-io

2.6

commons-lang

commons-lang

2.6

控制类 import org.apache.tomcat.util.http.fileupload.servlet.ServletFileUpload;

import org.springframework.web.bind.annotation.RequestMapping;

import org.springframework.web.bind.annotation.RestController;

import javax.servlet.http.HttpServletRequest;

import javax.servlet.http.HttpServletResponse;

import java.io.File;

@RestController

@RequestMapping("/chunk")

public class ChunkController {

@RequestMapping("/chunkUpload")

public StdOut chunkUpload(MultipartFileParam param, HttpServletRequest request, HttpServletResponse response) {

StdOut out = new StdOut();

File file = new File("C:\\chunk_test");//存储路径

ChunkService chunkService = new ChunkService();

String path = file.getAbsolutePath();

response.setContentType("text/html;charset=UTF-8");

try {

//判断前端Form表单格式是否支持文件上传

boolean isMultipart = ServletFileUpload.isMultipartContent(request);

if (!isMultipart) {

out.setCode(StdOut.PARAMETER_NULL);

out.setMessage("表单格式错误");

return out;

} else {

param.setTaskId(param.getIdentifier());

out.setModel(chunkService.chunkUploadByMappedByteBuffer(param, path));

return out;

}

} catch (NotSameFileExpection e) {

out.setCode(StdOut.FAIL);

out.setMessage("MD5校验失败");

return out;

} catch (Exception e) {

out.setCode(StdOut.FAIL);

out.setMessage("上传失败");

return out;

}

}

}

StdOut类(只是封装的返回类) public class StdOut {

public static final int SUCCESS = 200;

public static final int FAIL = 400;

public static final int PARAMETER_NULL = 500;

public static final int NO_LOGIN = 600;

private int code = 200;

private Object model = null;

private String message = null;

public StdOut() {

this.setCode(200);

this.setModel((Object)null);

}

public StdOut(int code) {

this.setCode(code);

this.setModel((Object)null);

}

public StdOut(List> model) {

this.setCode(200);

this.setModel(model);

}

public StdOut(int code, List> model) {

this.setCode(code);

this.setModel(model);

}

public int getCode() {

return this.code;

}

public void setCode(int code) {

this.code = code;

}

public String toString() {

return JSON.toJSONString(this);

}

public Object getModel() {

return this.model;

}

public void setModel(Object model) {

this.model = model;

}

public String getMessage() {

return this.message;

}

public void setMessage(String message) {

this.message = message;

}

}

MultipartFileParam类(文件信息类) import org.springframework.web.multipart.MultipartFile;

public class MultipartFileParam {

private String taskId;

private int chunkNumber;

private long chunkSize;

private int totalChunks;

private String identifier;

private MultipartFile file;

public String getTaskId() {

return taskId;

}

public void setTaskId(String taskId) {

this.taskId = taskId;

}

public int getChunkNumber() {

return chunkNumber;

}

public void setChunkNumber(int chunkNumber) {

this.chunkNumber = chunkNumber;

}

public long getChunkSize() {

return chunkSize;

}

public void setChunkSize(long chunkSize) {

this.chunkSize = chunkSize;

}

public int getTotalChunks() {

return totalChunks;

}

public void setTotalChunks(int totalChunks) {

this.totalChunks = totalChunks;

}

public String getIdentifier() {

return identifier;

}

public void setIdentifier(String identifier) {

this.identifier = identifier;

}

public MultipartFile getFile() {

return file;

}

public void setFile(MultipartFile file) {

this.file = file;

}

}

ChunkService类 import org.apache.commons.codec.digest.DigestUtils;

import org.apache.commons.io.FileUtils;

import org.apache.commons.lang.StringUtils;

import java.io.File;

import java.io.FileInputStream;

import java.io.IOException;

import java.io.RandomAccessFile;

import java.nio.ByteBuffer;

import java.nio.channels.FileChannel;

import java.util.UUID;

public class ChunkService {

public String chunkUploadByMappedByteBuffer(MultipartFileParam param, String filePath) throws IOException, NotSameFileExpection {

if (param.getTaskId() == null || "".equals(param.getTaskId())) {

param.setTaskId(UUID.randomUUID().toString());

}

String fileName = param.getFile().getOriginalFilename();

String tempFileName = param.getTaskId() + fileName.substring(fileName.lastIndexOf(".")) + "_tmp";

File fileDir = new File(filePath);

if (!fileDir.exists()) {

fileDir.mkdirs();

}

File tempFile = new File(filePath, tempFileName);

//第一步 打开将要写入的文件

RandomAccessFile raf = new RandomAccessFile(tempFile, "rw");

//第二步 打开通道

FileChannel fileChannel = raf.getChannel();

//第三步 计算偏移量

long position = (param.getChunkNumber() - 1) * param.getChunkSize();

//第四步 获取分片数据

byte[] fileData = param.getFile().getBytes();

//第五步 写入数据

fileChannel.position(position);

fileChannel.write(ByteBuffer.wrap(fileData));

fileChannel.force(true);

fileChannel.close();

raf.close();

//判断是否完成文件的传输并进行校验与重命名

boolean isComplete = checkUploadStatus(param, fileName, filePath);

if (isComplete) {

FileInputStream fileInputStream = new FileInputStream(tempFile.getPath());

String md5 = DigestUtils.md5Hex(fileInputStream);

fileInputStream.close();

if (StringUtils.isNotBlank(md5) && !md5.equals(param.getIdentifier())) {

throw new NotSameFileExpection();

}

renameFile(tempFile, fileName);

return fileName;

}

return null;

}

public void renameFile(File toBeRenamed, String toFileNewName) {

if (!toBeRenamed.exists() || toBeRenamed.isDirectory()) {

System.err.println("文件不存在");

return;

}

String p = toBeRenamed.getParent();

File newFile = new File(p + File.separatorChar + toFileNewName);

toBeRenamed.renameTo(newFile);

}

public boolean checkUploadStatus(MultipartFileParam param, String fileName, String filePath) throws IOException {

File confFile = new File(filePath, fileName + ".conf");

RandomAccessFile confAccessFile = new RandomAccessFile(confFile, "rw");

//设置文件长度

confAccessFile.setLength(param.getTotalChunks());

//设置起始偏移量

confAccessFile.seek(param.getChunkNumber() - 1);

//将指定的一个字节写入文件中 127,

confAccessFile.write(Byte.MAX_VALUE);

byte[] completeStatusList = FileUtils.readFileToByteArray(confFile);

confAccessFile.close();//不关闭会造成无法占用

//创建conf文件文件长度为总分片数,每上传一个分块即向conf文件中写入一个127,那么没上传的位置就是默认的0,已上传的就是127

for (int i = 0; i < completeStatusList.length; i++) {

if (completeStatusList[i] != Byte.MAX_VALUE) {

return false;

}

}

confFile.delete();

return true;

}

}

Vue-simple-uploader是一个轻量级的vue文件上传组件,可以支持多文件上传以及上传进度和错误信息提示。 要使用vue-simple-uploader实现文件上传,需要先将文件夹打包为zip文件,然后使用vue-simple-uploader上传zip文件即可。 以下是实现步骤: 1.安装vue-simple-uploader ``` npm install vue-simple-uploader --save ``` 2.在Vue组件中引入Vue-simple-uploader ``` import Uploader from 'vue-simple-uploader' export default { components: { Uploader } } ``` 3.在Vue组件中使用Vue-simple-uploader ``` <template> <div> <uploader ref="uploader" :upload-url="uploadUrl" :headers="headers" :data="formData" :multiple="false" :extensions="extensions" :auto-upload="false" @file-added="onFileAdded" @file-removed="onFileRemoved" @uploading="onUploading" @upload-success="onUploadSuccess" @upload-error="onUploadError" @upload-complete="onUploadComplete" /> <button @click="uploadFolder">上传文件夹</button> </div> </template> ``` 4.在Vue组件中实现上传文件夹的方法 ``` methods: { uploadFolder() { // 将文件夹打包为zip文件 let zip = new JSZip() let folder = zip.folder('folder') // 添加文件夹中的文件 folder.file('file1.txt', 'content1') folder.file('file2.txt', 'content2') // 生成zip文件 zip.generateAsync({ type: 'blob' }).then((blob) => { // 将zip文件上传 let file = new File([blob], 'folder.zip') this.$refs.uploader.add([file]) this.$refs.uploader.upload() }) }, onUploadSuccess(response, file, fileList) { // 上传成功回调函数 console.log(response) }, onUploadError(error, file, fileList) { // 上传失败回调函数 console.log(error) } } ``` 上述代码中,使用JSZip文件夹打包为zip文件,然后将zip文件添加到vue-simple-uploader中,最后调用upload方法上传文件上传成功或失败后,会分别调用onUploadSuccess和onUploadError回调函数。 需要注意的是,由于JSZip依赖于浏览器的原生Zip API,因此在一些不支持Zip API的浏览器上可能无法使用。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值