Netty: websocket 文件分段上传

简介

前端用 Blob 分割文件,定义好每一段数据的大小,首先要把文件信息,包括文件名、文件大小、校验值、分段大小等信息发给服务器。然后是执行每一段的发送任务,可异步(有待研究,是开多个ws连接还是?🤔)也可同步(服务端最好要有回应事件),异步的话需要注意文件段的顺序,最好的话同步异步都用自己定义个格式帧去传输,方便校验。这里是简单版的,直接发送,没有校验! 还有,分段下载也是一样的操作~

前端

前端用的是 ant-design-vue upload组件customRequest自定义上传。

<a-upload
                      name="file"
                      :file-list="fileList"
                      :customRequest="customRequest"
                      @change="handleChange"
                  >
                    <a-button>
                      <a-icon type="upload"/>
                      Click to Upload
                    </a-button>
                  </a-upload>

methods

customRequest (options) {
      const blob = options.file
      // 每个文件切片大小
      const bytesPerPiece = 40960

      let start = 0
      let end
      while (start < blob.size) {
        end = start + bytesPerPiece
        if (end > blob.size) {
          end = blob.size
        }
        // 切割文件
        const chunk = blob.slice(start, end)
        // 发送数据到服务器
        this.$global.ws.send(chunk)
        start = end
      }
      // 表示发送完毕
      this.$global.ws.send(new Blob(['end'], {type: 'text/plain'}))

      // 表示已经上传成功 ...
      const reader = new FileReader()
      reader.readAsDataURL(options.file)
      reader.onload = () => {
        options.onSuccess()
      }
    }

效果

前端发送数据

在这里插入图片描述

后台接收数据

在这里插入图片描述
后台代码

package com.xxx.sdk.server;

import com.xxx.sdk.protocol.r2000.upgrade.ReadFile;
import com.xxx.sdk.util.AsciiUtil;
import io.netty.buffer.ByteBuf;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.SimpleChannelInboundHandler;
import io.netty.handler.codec.http.websocketx.BinaryWebSocketFrame;

import java.io.ByteArrayInputStream;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.atomic.AtomicInteger;

/**
 * @author Rubin
 * @version v1 2020/12/11 14:19
 */
public class WsUploadFileHandler extends SimpleChannelInboundHandler<BinaryWebSocketFrame> {

    /**
     * 分段数据
     */
    List segmentedData = new ArrayList<>();

    /**
     * 文件总字节数
     */
    AtomicInteger fileLength = new AtomicInteger(0);

    @Override
    protected void channelRead0(ChannelHandlerContext ctx, BinaryWebSocketFrame msg) {
        ByteBuf content = msg.content();
        int readableBytes = content.readableBytes();

        byte[] bytes = new byte[readableBytes];
        content.getBytes(0, bytes);

        if (readableBytes == 3) {
            // 文件传输完毕
            if ("end".equals(AsciiUtil.hex2AsciiStr(bytes))) {

                // 拼接分段的数据
                byte[] file = new byte[fileLength.get()];
                // 拷贝下标
                AtomicInteger fileIndex = new AtomicInteger();
                segmentedData.forEach(fbs -> {
                    int length = ((byte[]) fbs).length;
                    System.arraycopy(fbs, 0, file, fileIndex.get(), length);
                    fileIndex.addAndGet(length);
                });
                // 读取完整文件
                ReadFile.read(new ByteArrayInputStream(file));
            }

        } else {
            segmentedData.add(bytes);
            fileLength.addAndGet(bytes.length);
        }
    }
}

楼主的是单机本地程序,所以是理想环境,校验啥的都没有,网络应用的话数据完整性校验可不能少!

大文件的话还可以做个进度条啥的,哈哈哈😄