之前写过一篇springboot下webSocket实现案例
java是可以直接支持上传文件的,但是一些大文件,比如好几G的那种,就需要分段上传了,可以利用websocket来实现,前端可以显示进度条信息。
先看实现后的效果图:
websocket分段上传大文件演示效果.gif
思路:
利用websocket直接传输message,把文件分段保存在message中,后台接收保存文件返回链接。
1.前端点击上传文件触发websocket连接,连接成功后分段文件并上传
2.后台拿到file_content,去掉data:application/octet-stream;base64,
前缀,然后base64解密得到byte[],追加保存至文件并返回保存状态信息,前端继续上传,后台继续保存直至文件全部发送完为止。
实现代码
前端(vue3+Bootstrap5为例,框架配置省略没贴出来)
<template>
<div class="container">
<div class="d-flex justify-content-center">
<div class=" flex-fill"></div>
<div class=" flex-fill"></div>
<div class=" flex-fill"></div>
<div class=" flex-fill"></div>
<div class=" flex-fill"></div>
<div class=" flex-fill"></div>
<div class="flex-fill">
<form action="" enctype="multipart/form-data">
<div class="mb-3 mt-3">
<label class="form-label">上传文件:</label>
<input type="file" id="file" class="form-control" v-on:change="upload($event)">
</div>
<div class="progress" style="height:10%;" v-if="status == 1">
<div class="progress-bar" :style="{width: file_status + '%'}">{{file_status}}%</div>
</div>
</form>
</div>
<div class=" flex-fill"></div>
<div class=" flex-fill"></div>
<div class=" flex-fill"></div>
<div class=" flex-fill"></div>
<div class=" flex-fill"></div>
<div class=" flex-fill"></div>
</div>
</div>
</template>
<script>
export default {
name: 'websocket',
data () {
return {
file: null,
sessionId:'',
timestamp:'',
noncestr: '',
file_name: '',
file_total_size: 0, //文件总大小
file_current_size: 0, //当前分段文件大小
file_sum_size: 0, //总分段文件大小
file_type: '',
file_content: '',
file_md5: '',
file_status: '', //文件百分比 file_sum_size*100/file_total_size
startSize: 0, //分段起始位置
endSize: 0, //分段结束位置
paragraph: 50 * 1024, // 每次分片传输文件的大小 50KB
websocket: null,
status: 0, //0未上传 1上传中
paragraphSize: 0,
reader: null,
file_path: '',
}
},
methods: {
upload(e) {
console.log(111, e.target.files);
let thisE = this;
if(e.target.files.length == 0){
console.log("ws", thisE.websocket);
if(thisE.websocket != null) {
thisE.websocket.close();
}
return;
}
if(e.target.files.length > 1){
alertCommon("提示", "一次性只能上传一个文件!");
return;
}
thisE.file = e.target.files[0];
thisE.sessionId = guuid();
thisE.file_name = thisE.file.name.substring(0, thisE.file.name.lastIndexOf('.'));
thisE.file_type = thisE.file.name.substring(thisE.file.name.lastIndexOf('.'));
thisE.file_total_size = thisE.file.size;
thisE.paragraphSize = Math.ceil(thisE.file_total_size/thisE.paragraph);
console.log("paragraphSize", thisE.paragraphSize);
console.log('value', e.target)
if(thisE.file.name.indexOf(".") == -1) {
alertCommon("提示", "不支持的文件格式!");
e.target.value = "";
return;
}
thisE.createWebsocket();
},
createWebsocket() {
let thisE = this;
let socketUrl = "ws://"+location.host+ "/socket/ws/chat?r=" + Date.parse(new Date());
socketUrl = socketUrl.replace("https", "ws").replace("http", "ws");
console.log(socketUrl);
if (thisE.websocket != null) {
thisE.websocket.close();
thisE.websocket = null;
}
if ('WebSocket' in window) {
thisE.websocket = new WebSocket(socketUrl);
} else {
alertCommon('提示','当前浏览器 Not support websocket');
return;
}
//连接发生错误的回调方法
thisE.websocket.onerror = function() {
console.log("WebSocket连接发生错误");
}
//连接成功建立的回调方法
thisE.websocket.onopen = function() {
console.log("WebSocket连接成功");
thisE.status = 1;
thisE.uploadFile();
}
//接收到消息的回调方法
thisE.websocket.onmessage = function(event) {
thisE.webSocketResponse(event);
}
//连接关闭的回调方法
thisE.websocket.onclose = function() {
console.log("WebSocket连接关闭");
thisE.reset();
}
//监听窗口关闭事件,当窗口关闭时,主动去关闭websocket连接,防止连接还没断开就关闭窗口,server端会抛异常。
window.onbeforeunload = function() {
thisE.websocket.close();
}
},
uploadFile() {
let thisE = this;
//拆分文件 分段上传
// console.log(222,guuid());
//this.noncestr = guuid();
thisE.reader = new FileReader();
thisE.endSize = thisE.startSize + thisE.paragraph;
if(thisE.endSize > thisE.file_total_size) {
thisE.endSize = thisE.file_total_size;
}
thisE.file_current_size = thisE.endSize - thisE.startSize;
thisE.file_sum_size = thisE.endSize;
thisE.file_status = Math.floor(thisE.file_sum_size * 100 / thisE.file_total_size);
let blob = null;
let file = thisE.file;
if (file.webkitSlice) {
//webkit浏览器
blob = file.webkitSlice(thisE.startSize, thisE.endSize);
} else {
blob = file.slice(thisE.startSize, thisE.endSize);
}
thisE.reader.readAsDataURL(blob);
thisE.reader.onload = function(evt) {
if(thisE.status == 0) { // 已取消或未开始
thisE.reset();
}
let params = {
sessionId: thisE.sessionId,
timestamp: Date.parse(new Date()),
noncestr: guuid(),
file_name: thisE.file_name,
file_total_size: thisE.file_total_size,
file_current_size: thisE.file_current_size,
file_sum_size: thisE.file_sum_size,
file_type: thisE.file_type,
file_content: evt.target.result,
file_md5: thisE.file_md5,
file_status: thisE.file_status
}
thisE.startSize = thisE.endSize;
thisE.websocket.send(JSON.stringify(params));
}
},
webSocketResponse(event) {
let thisE = this;
let response = JSON.parse(event.data);
console.log("response", response);
if(response.code != 1) {
alertCommon("错误", response.msg);
thisE.websocket.close();
return;
} else {
let data = response.data;
thisE.file_md5 = data.file_md5;
if(data.file_sum_size != thisE.file_total_size) {
thisE.uploadFile();
} else{
thisE.file_path = data.file_path_name;
console.log('上传成功,后端返回文件地址为:' + thisE.file_path);
thisE.websocket.close();
}
}
},
reset() {
this.status = 0;
this.file = null;
this.startSize = 0;
this.endSize = 0;
this.file_sum_size = 0;
this.file_path = '';
this.reader = null;
}
}
}
</script>
<!-- Add "scoped" attribute to limit CSS to this component only -->
<style scoped>
h1, h2 {
font-weight: normal;
}
ul {
list-style-type: none;
padding: 0;
}
li {
display: inline-block;
margin: 0 10px;
}
a {
/* color: #42b983; */
color: #0a58ca;
text-decoration:none;
font-weight: bold;
}
.moneyFont {
font-size: x-large;
font-weight: bold;
}
.menuStyle {
padding-top:2%;
}
.errorMsg{
color:red;
font-size:large;
font-weight:bold;
}
</style>
依赖外部js方法:
function guuid() {
function S4() {
return (((1+Math.random())*0x10000)|0).toString(16).substring(1);
}
return (S4()+S4()+"-"+S4()+"-"+S4()+"-"+S4()+"-"+S4()+S4()+S4());
}
路由配置:
在vue.config.js里配置如下:
module.exports = {
publicPath:
process.env.NODE_ENV === 'production' ? './' : '/',
devServer: {
proxy: {
'/socket': {
target: 'ws://127.0.0.1:8082/', //这里后台的地址模拟的;应该填写你们真实的后台接口
changOrigin: true, //允许跨域
ws: true, //是否代理 websockets
secure: false, //是否https接口
pathRewrite: {
/* 重写路径,当我们在浏览器中看到请求的地址为:http://localhost:8080/socket/core/getData/userInfo 时
实际上访问的地址是:http://121.121.67.254:8185/core/getData/userInfo,因为重写了 /socket
*/
'^/socket': ''
}
}
}
}
};
如果是外部nginx转发的,贴上nginx路由配置:
location /socket/ {
proxy_pass http://localhost:8082/;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "upgrade";
proxy_set_header X-real-ip $remote_addr;
proxy_set_header X-Forwarded-For $remote_addr;
}
上面代码可以看到 每次分段值为50kb,实际情况可以按需求改。
后端(springboot)
maven引入websocket包:
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-websocket</artifactId>
<version>2.6.2</version>
</dependency>
新建WebSocketConfig配置类 把ServerEndpointExporter注入spring管理并修改WebSocket传输的限制,如不修改,无法传输长字符串
package com.zhaohy.app.config;
import javax.servlet.ServletContext;
import javax.servlet.ServletException;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.boot.web.servlet.ServletContextInitializer;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.socket.server.standard.ServerEndpointExporter;
import org.springframework.web.util.WebAppRootListener;
@Configuration
public class WebSocketConfig implements ServletContextInitializer {
private static Logger logger = LoggerFactory.getLogger(WebSocketConfig.class);
/**
* ServerEndpointExporter 作用
* 这个Bean会自动注册使用@ServerEndpoint注解声明的websocket endpoint
* @return
*/
@Bean
public ServerEndpointExporter serverEndpointExporter() {
logger.info("22222222222222=====");
return new ServerEndpointExporter();
}
//在此处修改WebSocket传输的限制,如不修改,无法传输长字符串
@Override
public void onStartup(ServletContext servletContext) throws ServletException {
servletContext.addListener(WebAppRootListener.class);
servletContext.setInitParameter("org.apache.tomcat.websocket.textBufferSize","52428800");
servletContext.setInitParameter("org.apache.tomcat.websocket.binaryBufferSize","52428800");
}
}
编写WebSocketServer:
package com.zhaohy.app.controller;
import java.io.IOException;
import java.util.concurrent.CopyOnWriteArraySet;
import javax.websocket.OnClose;
import javax.websocket.OnError;
import javax.websocket.OnMessage;
import javax.websocket.OnOpen;
import javax.websocket.Session;
import javax.websocket.server.ServerEndpoint;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import com.zhaohy.app.entity.ResponseVO;
import com.zhaohy.app.entity.UploadBigFileParams;
import com.zhaohy.app.entity.UploadFileVO;
import com.zhaohy.app.service.WebSocketServerService;
import com.zhaohy.app.utils.AppFrameworkUtil;
import com.zhaohy.app.utils.JsonUtils;
/**
* @ServerEndpoint 注解是一个类层次的注解,它的功能主要是将目前的类定义成一个websocket服务器端,
* 注解的值将被用于监听用户连接的终端访问URL地址,客户端可以通过这个URL来连接到WebSocket服务器端
*/
@Component
@ServerEndpoint("/ws/chat")
public class WebSocketServer {
private static Logger logger = LoggerFactory.getLogger(WebSocketServer.class);
private static WebSocketServerService wsService;
//静态变量,用来记录当前在线连接数。应该把它设计成线程安全的。
private static int onlineCount = 0;
//concurrent包的线程安全Set,用来存放每个客户端对应的MyWebSocket对象。若要实现服务端与单一客户端通信的话,可以使用Map来存放,其中Key可以为用户标识
private static CopyOnWriteArraySet<WebSocketServer> webSocketSet = new CopyOnWriteArraySet<WebSocketServer>();
//与某个客户端的连接会话,需要通过它来给客户端发送数据
private Session session;
/**
* 连接建立成功调用的方法
* @param session 可选的参数。session为与某个客户端的连接会话,需要通过它来给客户端发送数据
*/
@OnOpen
public void onOpen(Session session){
this.session = session;
webSocketSet.add(this); //加入set中
logger.info("有新连接加入!当前在线人数为" + webSocketSet.size());
}
/**
* 连接关闭调用的方法
*/
@OnClose
public void onClose(){
webSocketSet.remove(this); //从set中删除
logger.info("有一连接关闭!当前在线人数为" + webSocketSet.size());
}
/**
* 收到客户端消息后调用的方法
* @param message 客户端发送过来的消息
* @param session 可选的参数
*/
@OnMessage
public void onMessage(String message, Session session) {
logger.info("来自客户端的消息:{}", message);
UploadBigFileParams params = JsonUtils.readValue(message, UploadBigFileParams.class);
if(null != params && !AppFrameworkUtil.isBlank(params.getSessionId())) {
// IWebSocketServerService wsService = (WebSocketServerService) ContextLoader.getCurrentWebApplicationContext().getBean("WebSocketServerService");
ResponseVO<UploadFileVO> vo = wsService.uploadBigFile(params, message);
try {
sendMessage(JsonUtils.writeValue(vo));
} catch (IOException e) {
logger.info("给客户端sendMessage抛异常:{}", e);
}
}
//群发消息
// for(WebSocketServer item: webSocketSet){
// try {
// item.sendMessage(message);
// } catch (IOException e) {
// e.printStackTrace();
// continue;
// }
// }
}
/**
* 发生错误时调用
* @param session
* @param error
*/
@OnError
public void onError(Session session, Throwable error){
logger.info("发生错误:{}", error);
error.printStackTrace();
}
/**
* 这个方法与上面几个方法不一样。没有用注解,是根据自己需要添加的方法。
* @param message
* @throws IOException
*/
public void sendMessage(String message) throws IOException{
this.session.getBasicRemote().sendText(message);
//this.session.getAsyncRemote().sendText(message);
}
public static synchronized int getOnlineCount() {
return onlineCount;
}
public static synchronized void addOnlineCount() {
WebSocketServer.onlineCount++;
}
public static synchronized void subOnlineCount() {
WebSocketServer.onlineCount--;
}
@Autowired
public void setWebSocketServerService(WebSocketServerService wsService) {
WebSocketServer.wsService = wsService;
}
}
创建WebSocketServerService业务接口:
package com.zhaohy.app.service;
import com.zhaohy.app.entity.ResponseVO;
import com.zhaohy.app.entity.UploadBigFileParams;
import com.zhaohy.app.entity.UploadFileVO;
public interface WebSocketServerService {
ResponseVO<UploadFileVO> uploadBigFile(UploadBigFileParams params, String message);
}
编写WebSocketServerServiceImpl实现类:
package com.zhaohy.app.service.impl;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import org.apache.commons.codec.binary.Base64;
import org.apache.commons.codec.digest.DigestUtils;
import org.springframework.stereotype.Service;
import com.zhaohy.app.entity.ResponseVO;
import com.zhaohy.app.entity.UploadBigFileParams;
import com.zhaohy.app.entity.UploadFileVO;
import com.zhaohy.app.enums.ErrorCode;
import com.zhaohy.app.service.WebSocketServerService;
import com.zhaohy.app.utils.FileUtil;
import com.zhaohy.app.utils.HashAlgorithm;
import com.zhaohy.app.utils.HashUtils;
import com.zhaohy.app.utils.LogUtils;
@Service("WebSocketServerService")
public class WebSocketServerServiceImpl implements WebSocketServerService {
// @Autowired
// private StringRedisTemplate redisTemplate;
@Override
public ResponseVO<UploadFileVO> uploadBigFile(UploadBigFileParams params, String message) {
UploadFileVO vo = new UploadFileVO();
vo.setFileName(params.getFileName());
vo.setFileTotalSize(params.getFileTotalSize().toString());
vo.setFileType(params.getFileType());
String sessionId = params.getSessionId();
String fileContent = params.getFileContent().replace("data:application/octet-stream;base64,", "");
File dirFile = new File(sessionId);
if(!dirFile.exists()) {
dirFile.mkdir();
}
String dirPath = dirFile.getAbsolutePath();
String fileName = "";
if(params.getFileType().contains(".")) {
fileName = params.getFileName() + params.getFileType();
} else {
fileName = params.getFileName() + "." + params.getFileType();
}
String fileOutPath = dirPath + "/" + fileName;
String tmpFilePath = fileOutPath + "_tmp_" + params.getTimestamp();
byte[] bytes = Base64.decodeBase64(fileContent);
//下载到临时文件 校验md5 校验file_current_size 计算FileSumSize 计算fileStatus
vo.setFileMd5(DigestUtils.md5Hex(bytes));
try {
FileUtil.downloadBytes(bytes, tmpFilePath);
} catch (Exception e) {
LogUtils.info("下载临时文件downloadBytes出错:{}", e);
return new ResponseVO<>(ErrorCode.ERROR_2009.getCode(), "下载临时文件downloadBytes出错:" + e.getMessage());
}
try {
FileUtil.appendFile(bytes, fileOutPath);
} catch (Exception e) {
LogUtils.info("追加文件appendFile出错:{}", e);
return new ResponseVO<>(ErrorCode.ERROR_2009.getCode(), "追加文件appendFile出错:" + e.getMessage());
}
InputStream is = null;
try {
is = new FileInputStream(new File(tmpFilePath));
vo.setFileCurrentSize(String.valueOf(is.available()));
} catch (Exception e) {
LogUtils.info("io出错:", e);
return new ResponseVO<>(ErrorCode.ERROR_2009.getCode(), "io出错:" + e.getMessage());
} finally {
try {
if(null != is)
is.close();
} catch (IOException e) {
LogUtils.info("io出错:", e);
return new ResponseVO<>(ErrorCode.ERROR_2009.getCode(), "io出错:" + e.getMessage());
}
}
try {
is = new FileInputStream(new File(fileOutPath));
vo.setFileSumSize(String.valueOf(is.available()));
} catch (Exception e) {
LogUtils.info("io出错:", e);
return new ResponseVO<>(ErrorCode.ERROR_2009.getCode(), "io出错:" + e.getMessage());
} finally {
try {
if(null != is)
is.close();
} catch (IOException e) {
LogUtils.info("io出错:", e);
return new ResponseVO<>(ErrorCode.ERROR_2009.getCode(), "io出错:" + e.getMessage());
}
}
System.out.println("vo.getFileSumSize():" + vo.getFileSumSize() + " " + "total:" + vo.getFileTotalSize());
Double fileStatus = Math.floor(Double.parseDouble(vo.getFileSumSize()) * 100/Double.parseDouble(vo.getFileTotalSize()));
vo.setFileStatus(fileStatus.toString());
if(100 != params.getFileStatus()) {
// redisTemplate.opsForValue().set(portalSessionId, message, 10, TimeUnit.MINUTES);
} else {
try {
vo.setFileMd5(HashUtils.getHashValue(fileOutPath, HashAlgorithm.MD5));
} catch (Exception e) {
LogUtils.info("计算md5出错:", e);
return new ResponseVO<>(ErrorCode.ERROR_2009.getCode(), "计算md5出错:" + e.getMessage());
}
vo.setFilePathName(fileOutPath);
//接下来可以上传到文件服务器 返回url给前端,这里逻辑省略
//删除临时文件夹
FileUtil.deleteDir(dirPath);
}
return new ResponseVO<>(vo);
}
public static void main(String[] args) {
String sumSize = "54025777";
String totalSize = "54025777";
Long fileStatus = Long.parseLong(sumSize) * 100/Long.parseLong(totalSize);
Double fileStatus1 = Math.floor(Double.parseDouble(sumSize) * 100/Double.parseDouble(totalSize));
System.out.println(fileStatus);
System.out.println(fileStatus1);
System.out.println(Integer.parseInt(sumSize));
System.out.println((Integer.parseInt(sumSize) * 100));
System.out.println(fileStatus);
int num = 54025777;
System.out.println(num*100);
}
}
补充外部依赖FileUtil工具类:
package com.zhaohy.app.utils;
import java.io.BufferedReader;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.RandomAccessFile;
import java.net.MalformedURLException;
import java.net.URL;
import java.net.URLConnection;
public class FileUtil {
/**
* txt格式转String
*
* @param txtPath
* @return
* @throws IOException
*/
public static String txtToStr(String txtPath) throws IOException {
StringBuilder buffer = new StringBuilder();
BufferedReader bf = null;
try {
bf = new BufferedReader(new InputStreamReader(new FileInputStream(txtPath), "UTF-8"));
String str = null;
while ((str = bf.readLine()) != null) {// 使用readLine方法,一次读一行
buffer.append(new String(str.getBytes(), "UTF-8"));
}
} finally {
if(null != bf) bf.close();
}
String xml = buffer.toString();
return xml;
}
public static Boolean downloadNet(String urlPath, String filePath) throws Exception {
Boolean flag = true;
int byteread = 0;
URL url;
try {
url = new URL(urlPath);
} catch (MalformedURLException e1) {
flag = false;
throw e1;
}
InputStream inStream = null;
FileOutputStream fs = null;
try {
URLConnection conn = url.openConnection();
inStream = conn.getInputStream();
fs = new FileOutputStream(filePath);
byte[] buffer = new byte[1024];
while ((byteread = inStream.read(buffer)) != -1) {
fs.write(buffer, 0, byteread);
}
} catch (Exception e) {
flag = false;
throw e;
} finally {
fs.close();
inStream.close();
}
return flag;
}
public static void downloadBytes(byte[] bytes, String filePath) throws Exception {
File file = new File(filePath);
if(!file.exists()) {
file.createNewFile();
}
FileOutputStream fs = null;
try {
fs = new FileOutputStream(filePath);
fs.write(bytes);
} finally {
fs.close();
}
}
public static void appendFile(byte[] bytes, String fileName) throws IOException {
// 打开一个随机访问文件流,按读写方式
RandomAccessFile randomFile = new RandomAccessFile(fileName, "rw");
// 文件长度,字节数
long fileLength = randomFile.length();
// 将写文件指针移到文件尾。
randomFile.seek(fileLength);
randomFile.write(bytes);
randomFile.close();
}
public static String getTmpDir() {
String tmpDir = "/tmp/";
String os = System.getProperty("os.name");
if (os.toLowerCase().contains("windows")) {
tmpDir = "D://tmp/";
File file = new File(tmpDir);
if (!file.exists())
file.mkdir();
}
return tmpDir;
}
public static void mkdir(String path) {
File file = new File(path);
if (!file.exists()) {
file.mkdir();
} else if (!file.isDirectory()) {
file.mkdir();
}
}
public static void deleteDir(String dirPath) {
File file = new File(dirPath);
File[] fileList = file.listFiles();
for (File f : fileList) {
if (f.exists()) {
if (f.isDirectory()) {
deleteDir(f.getAbsolutePath());
} else {
f.delete();
}
}
}
if (file.exists())
file.delete();
}
public static void main(String[] args) throws Exception {
}
}
这里着重提一下,websocket里注入spring管理的业务类时不能直接用@Autowired,因为websocket是多对象,而spring是单例,所以要把成员变量用static修饰,在set方法上@Autowired
private static WebSocketServerService wsService;
@Autowired
public void setWebSocketServerService(WebSocketServerService wsService) {
WebSocketServer.wsService = wsService;
}
最后补充下ResponseVO实体类:
package com.zhaohy.app.entity;
import java.io.Serializable;
import com.zhaohy.app.enums.ErrorCode;
public class ResponseVO<T> implements Serializable {
/**
*
*/
private static final long serialVersionUID = 6699585076192089392L;
private String code;
private String msg;
private Object data;
public ResponseVO(String code, String msg, T data) {
super();
this.code = code;
this.msg = msg;
this.data = data;
}
public ResponseVO(String code, String msg) {
super();
this.code = code;
this.msg = msg;
}
public ResponseVO(T data) {
super();
this.code = ErrorCode.SUCCESS.getCode();
this.msg = ErrorCode.SUCCESS.getCn();
this.data = data;
}
public ResponseVO() {
super();
this.code = ErrorCode.SUCCESS.getCode();
this.msg = ErrorCode.SUCCESS.getCn();
}
public ResponseVO(ErrorCode code) {
super();
this.code = code.getCode();
this.msg = code.getCn();
}
public String getCode() {
return code;
}
public void setCode(String code) {
this.code = code;
}
public String getMsg() {
return msg;
}
public void setMsg(String msg) {
this.msg = msg;
}
public Object getData() {
return data;
}
public void setData(T data) {
this.data = data;
}
}
前后端交互json格式设计
前端请求
{
"sessionId": "245e67ac-eefc-d589-785c-7e178c70cffe",
"timestamp": 1649923766000,
"noncestr": "39963e92-9fc2-8ddd-fbe4-2a621769e74b",
"file_name": "springbootSSM-imageSave-0.0.1-SNAPSHOT",
"file_total_size": 54025777,
"file_current_size": 9777,
"file_sum_size": 54025777,
"file_type": ".jar",
"file_content": "data:application/octet-stream;base64,xxxxxxxxxxxx",
"file_md5": "a7f87af5fe9f3ae05107388a681a2055",
"file_status": 100
}
后端返回:
{
"code": "1",
"msg": "成功",
"data": {
"file_name": "springbootSSM-imageSave-0.0.1-SNAPSHOT",
"file_current_size": "9777",
"file_sum_size": "54025777",
"file_total_size": "54025777",
"file_type": ".jar",
"file_md5": "6030277b5ba6c076040310a32fc48c0f",
"file_status": "100.0",
"file_path_name": "D:\\work\\ownspace\\mycode\\springbootSSM-imageSave\\245e67ac-eefc-d589-785c-7e178c70cffe/springbootSSM-imageSave-0.0.1-SNAPSHOT.jar"
}
}
补充下参数UploadBigFileParams实体类:
package com.zhaohy.app.entity;
import com.fasterxml.jackson.annotation.JsonProperty;
public class UploadBigFileParams {
private String sessionId;
private Long timestamp;
private String noncestr;
@JsonProperty("file_name")
private String fileName;
@JsonProperty("file_total_size")
private Long fileTotalSize;
@JsonProperty("file_current_size")
private Long fileCurrentSize;
@JsonProperty("file_sum_size")
private Long fileSumSize;
@JsonProperty("file_type")
private String fileType;
@JsonProperty("file_content")
private String fileContent;
@JsonProperty("file_md5")
private String fileMd5;
@JsonProperty("file_status")
private Integer fileStatus;
public String getSessionId() {
return sessionId;
}
public void setSessionId(String sessionId) {
this.sessionId = sessionId;
}
public Long getTimestamp() {
return timestamp;
}
public void setTimestamp(Long timestamp) {
this.timestamp = timestamp;
}
public String getNoncestr() {
return noncestr;
}
public void setNoncestr(String noncestr) {
this.noncestr = noncestr;
}
public String getFileName() {
return fileName;
}
public void setFileName(String fileName) {
this.fileName = fileName;
}
public Long getFileTotalSize() {
return fileTotalSize;
}
public void setFileTotalSize(Long fileTotalSize) {
this.fileTotalSize = fileTotalSize;
}
public Long getFileCurrentSize() {
return fileCurrentSize;
}
public void setFileCurrentSize(Long fileCurrentSize) {
this.fileCurrentSize = fileCurrentSize;
}
public Long getFileSumSize() {
return fileSumSize;
}
public void setFileSumSize(Long fileSumSize) {
this.fileSumSize = fileSumSize;
}
public String getFileType() {
return fileType;
}
public void setFileType(String fileType) {
this.fileType = fileType;
}
public String getFileContent() {
return fileContent;
}
public void setFileContent(String fileContent) {
this.fileContent = fileContent;
}
public String getFileMd5() {
return fileMd5;
}
public void setFileMd5(String fileMd5) {
this.fileMd5 = fileMd5;
}
public Integer getFileStatus() {
return fileStatus;
}
public void setFileStatus(Integer fileStatus) {
this.fileStatus = fileStatus;
}
}
后端UploadFileVO返回类:
package com.zhaohy.app.entity;
import com.fasterxml.jackson.annotation.JsonProperty;
public class UploadFileVO {
@JsonProperty("file_name")
private String fileName;
@JsonProperty("file_current_size")
private String fileCurrentSize;
@JsonProperty("file_sum_size")
private String fileSumSize;
@JsonProperty("file_total_size")
private String fileTotalSize;
@JsonProperty("file_type")
private String fileType;
@JsonProperty("file_md5")
private String fileMd5;
@JsonProperty("file_status")
private String fileStatus;
@JsonProperty("file_path_name")
private String filePathName;
public String getFileName() {
return fileName;
}
public void setFileName(String fileName) {
this.fileName = fileName;
}
public String getFileCurrentSize() {
return fileCurrentSize;
}
public void setFileCurrentSize(String fileCurrentSize) {
this.fileCurrentSize = fileCurrentSize;
}
public String getFileSumSize() {
return fileSumSize;
}
public void setFileSumSize(String fileSumSize) {
this.fileSumSize = fileSumSize;
}
public String getFileTotalSize() {
return fileTotalSize;
}
public void setFileTotalSize(String fileTotalSize) {
this.fileTotalSize = fileTotalSize;
}
public String getFileType() {
return fileType;
}
public void setFileType(String fileType) {
this.fileType = fileType;
}
public String getFileMd5() {
return fileMd5;
}
public void setFileMd5(String fileMd5) {
this.fileMd5 = fileMd5;
}
public String getFileStatus() {
return fileStatus;
}
public void setFileStatus(String fileStatus) {
this.fileStatus = fileStatus;
}
public String getFilePathName() {
return filePathName;
}
public void setFilePathName(String filePathName) {
this.filePathName = filePathName;
}
}
json处理工具类:
package com.zhaohy.app.utils;
import java.util.Objects;
import org.springframework.context.ApplicationContext;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.core.type.TypeReference;
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;
public class JsonUtils {
private static ObjectMapper defaultObjectMapper = new ObjectMapper();
/**
* 解析JSON
* @param src
* @param valueType
* @return
*/
public static <T> T readValue(String src, Class<T> valueType) {
ObjectMapper objectMapper = getObjectMapper();
try {
return objectMapper.readValue(src, valueType);
} catch (JsonProcessingException e) {
throw new RuntimeException(e);
}
}
/**
* 解析JSON
* @param src
* @param valueType
* @return
*/
public static <T> T readValue(String src, TypeReference<T> valueType) {
ObjectMapper objectMapper = getObjectMapper();
try {
return objectMapper.readValue(src, valueType);
} catch (JsonProcessingException e) {
throw new RuntimeException(e);
}
}
/**
* 解析JSON
* @param src
* @param valueType
* @return
*/
public static <T> T readValue(String src, String key, Class<T> valueType) {
ObjectMapper objectMapper = getObjectMapper();
try {
return objectMapper.convertValue(objectMapper.readTree(src).get(key), valueType);
} catch (JsonProcessingException e) {
throw new RuntimeException(e);
}
}
/**
* 解析JSON
* @param src
* @param valueType
* @return
*/
public static <T> T readValue(String src, String key, TypeReference<T> valueType) {
ObjectMapper objectMapper = getObjectMapper();
try {
return objectMapper.convertValue(objectMapper.readTree(src), valueType);
} catch (JsonProcessingException e) {
throw new RuntimeException(e);
}
}
/**
* 解析JSON
* @param src
* @return
*/
public static JsonNode readTree(String src) {
ObjectMapper objectMapper = getObjectMapper();
try {
return objectMapper.readTree(src);
} catch (JsonProcessingException e) {
throw new RuntimeException(e);
}
}
/**
* 转换JSON
* @param value
* @return
*/
public static String writeValue(Object value) {
ObjectMapper objectMapper = getObjectMapper();
try {
return objectMapper.writeValueAsString(value);
} catch (JsonProcessingException e) {
throw new RuntimeException(e);
}
}
private static ObjectMapper getObjectMapper() {
ApplicationContext applicationContext = SpringContextUtil.getApplicationContext();
if (Objects.nonNull(applicationContext)) {
ObjectMapper objectMapper = applicationContext.getBean(ObjectMapper.class);
if (Objects.nonNull(objectMapper)) {
return objectMapper;
}
}
return defaultObjectMapper;
}
}
至此应该比较全了,上面代码中没有把保存的文件上传到文件服务器的逻辑,可以在实际业务中自己加上。
完结散花~
网友评论