使用场景
无法直接登录服务器上传文件,使用web端上传超大文件出现超时
实现原理
上传
server端与client端建立websocket连接,client将待传文件进行分块,然后将文件的相关信息(文件名、md5值、分块大小、总块数、当前块数)及文件数据一并上传到服务端,服务端在本地建立文件通过追加的方式将上传的数据写入文件中,当当前块与总块数相等且文件MD5相同时认为文件上传成功
下载
与上传相反,将client当成服务端,client与server建立连接后,向服务端发送可接收请求,服务端收到后将文件进行分块处理并记录文件相关信息连同数据一并发送给client端,当服务端发现文件读取完毕后将块大小设置为-1,client端读取后进行文件md5校验,校验通过则认为下载成功。
代码展示
jdk版本: 14
项目地址: https://gitee.com/LovingL/big-file-upload-project
使用依赖
服务端
dependencies {
implementation('org.springframework.boot:spring-boot-starter-undertow')
implementation 'org.springframework.boot:spring-boot-starter-actuator'
implementation('org.springframework.boot:spring-boot-starter-websocket') {
exclude module: 'spring-boot-starter-tomcat'
}
implementation 'cn.hutool:hutool-json:5.6.5'
implementation 'cn.hutool:hutool-crypto:5.6.5'
compileOnly 'org.projectlombok:lombok'
developmentOnly 'org.springframework.boot:spring-boot-devtools'
runtimeOnly 'com.taobao.arthas:arthas-spring-boot-starter:3.4.8'
annotationProcessor 'org.projectlombok:lombok'
testImplementation('org.springframework.boot:spring-boot-starter-test') {
exclude group: 'org.junit.vintage', module: 'junit-vintage-engine'
}
}
客户端
dependencies {
implementation("org.java-websocket:Java-WebSocket:1.5.1")
implementation("cn.hutool:hutool-json:5.4.3")
implementation("cn.hutool:hutool-crypto:5.4.3")
implementation("org.slf4j:slf4j-jdk14:1.7.25")
compileOnly("org.projectlombok:lombok:1.18.12")
annotationProcessor("org.projectlombok:lombok:1.18.12")
testCompile group: 'junit', name: 'junit', version: '4.12'
}
消息定义
文件发送消息:
public class SendFileData {
private String fileName; //文件名
private int currentBlock; //当前块号
private int totalBlock; //总块号
private int blockSize; //块大小
private String md5sum; //md5值
private byte[] data; //文件数据
}
文件接收消息:
public class ReceiveFileData {
private int status; //接收状态码
private String errMsg; //错误消息返回
private String uri; //文件地址
}
文件上传
服务端
public void onMessage(Session session, String reqData) throws IOException {
SendFileData data = JSONUtil.toBean(reqData, SendFileData.class);
String filePath = StrUtil.format("{}/{}", fileConfig.getTempDir(), data.getFileName());
if (file == null) {
file = FileUtil.file(filePath);
if (!FileUtil.exist(file)) {
FileUtil.touch(file);
}
md5sum = MD5.create().digestHex16(file);
}
ReceiveFileData receiveFileData = new ReceiveFileData();
//先进行md5校验,如果md5已相同说明已经上传过了就直接返回服务端地址即可
if (md5sum.equals(data.getMd5sum())) {
receiveFileData.setStatus(HttpStatus.OK.value());
receiveFileData.setUri</