You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Java Socket传输文件后发送MD5校验失败,如何调整代码?

问题根源

服务端代码中使用while ((length = dis.read(bytes, 0, bytes.length)) != -1)循环读取文件内容,这个循环会持续阻塞直到客户端关闭输出流。但客户端发送完文件切片内容后,并未关闭流,而是继续发送MD5值,导致服务端一直卡在循环里,永远执行不到后续读取MD5的代码。

解决思路

在客户端发送文件切片内容前,先把切片的字节长度发送给服务端;服务端先读取这个长度,再根据长度精准读取对应字节数的文件内容,读完后就跳出循环,再去读取MD5值。

修改后的代码

客户端调整

在核心传输逻辑前,新增发送切片字节长度的代码:

File file = new File(filePath);
if (file.exists()) {
    this.rafin = new RandomAccessFile(filePath, "r");
    this.dos = new DataOutputStream(super.getOutputStream());
    rafin.seek(start); // 找到位置
    // 写入文件名
    dos.writeUTF(file.getName()+" ."+order);
    // 写入文件切片个数
    dos.writeInt(fileSplitNum);
    // 写入sendIp
    dos.writeUTF(localIp);
    // 写入文件落盘时间
    dos.writeUTF(lastModifiedDate);
    long fileLength = end - start + 1;
    // 新增:发送切片的字节长度
    dos.writeLong(fileLength);
    dos.flush();
    log.info("文件名为:"+file.getName()+" ."+order);
    
    byte[] bytes = new byte[1024 * 8];
    
    // 计算循环
    long times = fileLength / bytes.length;
    
    int length;
    long start = System.currentTimeMillis();
    // ======================核心传输逻辑==========================
    MessageDigest md5 = MessageDigest.getInstance("MD5");
    for (int i = 0;i < times; i++) {
        length = rafin.read(bytes, 0, bytes.length);
        md5.update(bytes, 0, length); // md5计算
        dos.write(bytes, 0, length);
    }
    long last = fileLength % bytes.length;
    if (last != 0) {
        length = rafin.read(bytes, 0, (int) last);
        md5.update(bytes, 0, (int) last); // md5
        dos.write(bytes, 0, length);
    }
    // ========================核心传输逻辑结束==============================
    dos.flush();
    String fileMd5 = DigestUtil.toHexString(md5.digest());
    // 将文件的md5编码传入server
     dos.writeUTF(fileMd5);
     dos.flush();
    log.info("第{}切片的md5加密密钥为:{}", order, fileMd5);
    long end = System.currentTimeMillis();
    
    String file_trans_stat = dis.readUTF();
    log.info("文件传输状态:{}", file_trans_stat);
    latch.countDown(); // latch锁减一
    log.info(String.format("%s 传输完毕,用时:%.2f秒%n",Thread.currentThread().getName(),(double)(end-start)/1000));
}

服务端调整

先读取切片长度,再根据长度精准读取内容,替换原有的无限循环:

dis = new DataInputStream(socket.getInputStream());
// 文件名
String fileName = dis.readUTF();
// 文件切片个数
int fileSplitNum = dis.readInt();
// sendIp
String sendIp = dis.readUTF();
// 文件落盘时间
String lastModifiedDate = dis.readUTF();
// 新增:读取切片的字节长度
long fileLength = dis.readLong();
// 计时表
ThreadLocal<StopWatch> threadLocal = new ThreadLocal<>();
threadLocal.set(new StopWatch());
threadLocal.get().start();
String fileNameTmp = fileName+".tmp";
log.info("接收文件夹为:{}", fileDir);
File directory = new File(fileDir);
if (!directory.exists()) {
    directory.mkdir();
}
File file = new File(directory.getAbsolutePath() + File.separatorChar + fileNameTmp);
FileOutputStream fos = new FileOutputStream(file);

byte[] bytes = new byte[1024 * 8];
int length;
long readTotal = 0;
MessageDigest md5 = MessageDigest.getInstance("MD5");
// 根据切片长度精准读取内容,不再依赖流结束标记
while (readTotal < fileLength) {
    // 计算本次最多能读的字节数,避免最后一次读多
    int readSize = (int) Math.min(bytes.length, fileLength - readTotal);
    length = dis.read(bytes, 0, readSize);
    fos.write(bytes, 0, length);
    md5.update(bytes, 0, length);
    fos.flush();
    readTotal += length;
}
fos.close();
// 现在可以正常读取MD5了
String clientFileMd5 = dis.readUTF();
log.info("client传来的{}的加密密钥为:{}", fileNameTmp, clientFileMd5);
String fileMd5 = DigestUtil.toHexString(md5.digest());
log.info("切片{}的md5加密密钥为: {}", fileNameTmp, fileMd5);
额外注意事项
  • 客户端发送的所有数据(包括长度、内容、MD5)必须和服务端读取顺序完全一致,否则会出现数据解析错误。
  • 后续交互(比如客户端等待服务端的传输状态)时,要确保流不会被提前关闭,避免通信中断。

内容的提问来源于stack exchange,提问作者Krlin Homon

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.27 16:45:02