美文网首页
java利用websocket实现分段上传大文件并显示进度信息

java利用websocket实现分段上传大文件并显示进度信息

作者: haiyong6 | 来源:发表于2022-04-16 11:30 被阅读0次

之前写过一篇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;
    }
}

至此应该比较全了,上面代码中没有把保存的文件上传到文件服务器的逻辑,可以在实际业务中自己加上。

完结散花~

相关文章

网友评论

      本文标题:java利用websocket实现分段上传大文件并显示进度信息

      本文链接:https://www.haomeiwen.com/subject/eacxertx.html