Java Socket 传输大文件指南
在Java中使用Socket传输大文件时,需要考虑几个关键问题:内存管理、传输效率和错误处理,以下是完整的实现方案:

(图片来源网络,侵删)
基本实现方案
服务器端代码
import java.io.*;
import java.net.*;
public class FileServer {
public static void main(String[] args) {
int port = 12345;
String filePath = "large_file.zip";
try (ServerSocket serverSocket = new ServerSocket(port);
Socket clientSocket = serverSocket.accept();
FileInputStream fis = new FileInputStream(filePath);
BufferedInputStream bis = new BufferedInputStream(fis);
OutputStream os = clientSocket.getOutputStream()) {
System.out.println("Client connected");
// 获取文件大小
File file = new File(filePath);
long fileSize = file.length();
// 发送文件大小
DataOutputStream dos = new DataOutputStream(os);
dos.writeLong(fileSize);
// 发送文件内容
byte[] buffer = new byte[8192]; // 8KB缓冲区
int bytesRead;
while ((bytesRead = bis.read(buffer)) != -1) {
os.write(buffer, 0, bytesRead);
}
System.out.println("File sent successfully");
} catch (IOException e) {
e.printStackTrace();
}
}
}
客户端代码
import java.io.*;
import java.net.*;
public class FileClient {
public static void main(String[] args) {
String serverAddress = "localhost";
int port = 12345;
String savePath = "received_file.zip";
try (Socket socket = new Socket(serverAddress, port);
InputStream is = socket.getInputStream();
FileOutputStream fos = new FileOutputStream(savePath);
BufferedOutputStream bos = new BufferedOutputStream(fos)) {
System.out.println("Connected to server");
// 接收文件大小
DataInputStream dis = new DataInputStream(is);
long fileSize = dis.readLong();
System.out.println("Receiving file of size: " + fileSize + " bytes");
// 接收文件内容
byte[] buffer = new byte[8192]; // 8KB缓冲区
long bytesReceived = 0;
int bytesRead;
while (bytesReceived < fileSize && (bytesRead = is.read(buffer)) != -1) {
bos.write(buffer, 0, bytesRead);
bytesReceived += bytesRead;
// 显示进度
int progress = (int) ((bytesReceived * 100) / fileSize);
System.out.print("\rProgress: " + progress + "%");
}
System.out.println("\nFile received successfully");
} catch (IOException e) {
e.printStackTrace();
}
}
}
优化方案
使用NIO提高性能
import java.io.*;
import java.net.*;
import java.nio.*;
import java.nio.channels.*;
public class NioFileServer {
public static void main(String[] args) throws IOException {
int port = 12345;
String filePath = "large_file.zip";
try (ServerSocketChannel serverChannel = ServerSocketChannel.open();
FileChannel fileChannel = FileChannel.open(Paths.get(filePath), StandardOpenOption.READ)) {
serverChannel.socket().bind(new InetSocketAddress(port));
System.out.println("Server started on port " + port);
try (SocketChannel clientChannel = serverChannel.accept()) {
System.out.println("Client connected");
// 发送文件大小
long fileSize = fileChannel.size();
ByteBuffer sizeBuffer = ByteBuffer.allocate(Long.BYTES);
sizeBuffer.putLong(fileSize);
sizeBuffer.flip();
clientChannel.write(sizeBuffer);
// 发送文件内容
fileChannel.transferTo(0, fileSize, clientChannel);
System.out.println("File sent successfully");
}
}
}
}
客户端断点续传
public class ResumableFileClient {
public static void main(String[] args) {
String serverAddress = "localhost";
int port = 12345;
String savePath = "received_file.zip";
long startPosition = 0; // 从0开始,或从上次中断的位置开始
try (RandomAccessFile raf = new RandomAccessFile(savePath, "rw");
Socket socket = new Socket(serverAddress, port);
InputStream is = socket.getInputStream()) {
raf.seek(startPosition);
// 接收文件大小
DataInputStream dis = new DataInputStream(is);
long fileSize = dis.readLong();
System.out.println("Receiving file of size: " + fileSize + " bytes");
// 接收文件内容
byte[] buffer = new byte[8192];
long bytesReceived = startPosition;
int bytesRead;
while (bytesReceived < fileSize && (bytesRead = is.read(buffer)) != -1) {
raf.write(buffer, 0, bytesRead);
bytesReceived += bytesRead;
// 显示进度
int progress = (int) ((bytesReceived * 100) / fileSize);
System.out.print("\rProgress: " + progress + "%");
}
System.out.println("\nFile received successfully");
} catch (IOException e) {
e.printStackTrace();
}
}
}
关键注意事项
-
缓冲区大小:根据网络条件和文件大小选择合适的缓冲区大小(通常4KB-64KB)
-
内存管理:
- 避免将整个文件读入内存
- 使用流式处理和缓冲区
-
错误处理:
- 添加超时机制
- 处理网络中断和异常
- 实现校验机制(如MD5或SHA校验)
-
性能优化:
(图片来源网络,侵删)- 使用NIO(New I/O)提高性能
- 考虑多线程处理
- 使用压缩减少传输数据量
-
安全性:
- 添加认证机制
- 使用SSL/TLS加密传输
- 验证文件完整性
-
进度显示:
- 实现进度回调或监听器
- 考虑多线程更新UI(如果是GUI应用)
完整示例(带进度和校验)
// 服务器端带校验
public class SecureFileServer {
public static void main(String[] args) throws Exception {
int port = 12345;
String filePath = "large_file.zip";
try (ServerSocket serverSocket = new ServerSocket(port);
Socket clientSocket = serverSocket.accept();
FileInputStream fis = new FileInputStream(filePath);
BufferedInputStream bis = new BufferedInputStream(fis);
OutputStream os = clientSocket.getOutputStream()) {
// 计算文件校验和
MessageDigest md = MessageDigest.getInstance("MD5");
DigestInputStream dis = new DigestInputStream(bis, md);
// 获取文件大小
File file = new File(filePath);
long fileSize = file.length();
// 发送文件大小和校验和长度
DataOutputStream dos = new DataOutputStream(os);
dos.writeLong(fileSize);
dos.writeInt(16); // MD5是16字节
// 发送文件内容
byte[] buffer = new byte[8192];
int bytesRead;
long totalBytesRead = 0;
while ((bytesRead = dis.read(buffer)) != -1) {
os.write(buffer, 0, bytesRead);
totalBytesRead += bytesRead;
// 显示服务器端进度
int progress = (int) ((totalBytesRead * 100) / fileSize);
System.out.print("\rSending: " + progress + "%");
}
// 发送校验和
byte[] digest = md.digest();
os.write(digest);
System.out.println("\nFile sent with checksum");
}
}
}
这个方案提供了从基础到高级的Java Socket文件传输实现,可以根据实际需求选择合适的方案。

(图片来源网络,侵删)
