上一篇文章我们实现聊天的功能,下面我们看文件传输怎么实现。
我的做法是:增加一个文件服务器,所有上传和下载文件的操作都由文件服务器来处理。
因此处理逻辑是这样的:如果用户请求上传文件或者下载文件,那么就将用户直接与文件服务器通信,而不用经过中央服务器。
所以现在的问题是知道java怎么实现上传和下载文件,如果这个问题解决了,那基本就搞定了。
首先,文件传输基本都是用面向连接的方式。因为无连接的方式容易丢包,一旦丢了一个数据包,文件就坏了,所有努力全白费。但是需要注意的是面向连接的方式,在服务器处理完一个连接后该连接就关闭了。
下面看代码,在原来的基础上我新建了两个主要文件:FileServer.java和FileClient..java。
package chat.net.file;
import java.io.BufferedInputStream;
import java.io.BufferedOutputStream;
import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.Iterator;
import java.util.Set;
import java.util.TreeMap;
import chat.Globals;
/**
* 文件服务器
*
* @author michael
*
*/
public class FileServer {
private TreeMap<String, String> fileMap = new TreeMap<>();
private final String SavePath = "save/";// 上传文件保存目录
private final int port = 8821;
private ServerSocket ss;
private Socket s;
private String sender;// 上传者名字
private String receiver;// 接收者名字
private int bufferSize = 8192;
public void start() {
try {
// 创建目录
File file = new File(SavePath);
if (!file.exists()) {
file.mkdir();
}
ss = new ServerSocket(port);
while (true) {
s = ss.accept();
DataInputStream dis = new DataInputStream(
new BufferedInputStream(s.getInputStream()));
dis.readByte();// 运行环境
int req = dis.readInt();
if (req == Globals.UploadReq) {// 用户上传文件
recvFile(dis);
} else {// 用户下载文件
sendFile(dis);
}
}
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
private void recvFile(DataInputStream dis) {
DataOutputStream dos = null;
try {
sender = dis.readUTF();// 发送者名字
String savePath = SavePath;
byte[] buf = new byte[bufferSize];
long len = 0;
String fileName = dis.readUTF();// 可能接收到终止的通知
if (fileName.equals(String.valueOf(Globals.Exit))) {
dis.close();
return;
}
savePath += fileName;
dos = new DataOutputStream(new BufferedOutputStream(
new BufferedOutputStream(new FileOutputStream(savePath))));
len = dis.readLong();
System.out.println("文件的长度为:" + len);
while (true) {
int read = 0;
if (dis != null) {
read = dis.read(buf);
}
if (read == -1) {
break;
}
dos.write(buf, 0, read);
}
fileMap.put(fileName, sender);
System.out.println("接收完成,文件存为" + savePath);
} catch (Exception e) {
e.printStackTrace();
} finally {
try {
dis.close();
if (dos != null) {
dos.close();
}
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
private void sendFile(DataInputStream dis) {
DataOutputStream dos = null;
DataInputStream fis = null;
try {
receiver = dis.readUTF();
dos = new DataOutputStream(s.getOutputStream());
// 给客户端发送文件列表
String fileList = "文件列表:\n";
if (fileMap.size() == 0) {
dos.writeUTF("");
return;
}
Set<String> set = fileMap.keySet();
Iterator<String> it = set.iterator();
String key;
int i = 0;
while (it.hasNext()) {
++i;
key = it.next();
fileList += i + ".<" + key + "," + fileMap.get(key) + ">";
}
dos.writeUTF(fileList);
String fileName = getFileName(fileList, dis.readInt());
File file = new File(SavePath + fileName);
// 开始发送文件
fis = new DataInputStream(new BufferedInputStream(
new FileInputStream(file)));
dos.writeUTF(fileName);
dos.flush();
dos.writeLong((long) file.length());
dos.flush();
byte[] buf = new byte[bufferSize];
int read = 0;
while (true) {
read = fis.read(buf);
if (read == -1) {
break;
}
dos.write(buf, 0, read);
}
dos.flush();
System.out.println("文件" + fileName + "传输完成");
} catch (Exception e) {
// TODO: handle exception
e.printStackTrace();
} finally {
try {
dis.close();
if (fis != null) {
fis.close();
}
dos.close();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
private String getFileName(String fileList, int no) {
String fileName = fileList.substring(fileList.indexOf(String
.valueOf(no)));
fileName = fileName.substring(fileName.indexOf("<") + 1,
fileName.indexOf(","));
return fileName;
}
public static void main(String[] args) {
new FileServer().start();
}
}
package chat.net.file;
import java.io.BufferedInputStream;
import java.io.BufferedOutputStream;
import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.net.Socket;
import java.net.UnknownHostException;
import java.util.Scanner;
import chat.Globals;
/**
* 文件客户端 支持上传和下载文件
*
* @author michael
*
*/
public class FileClient {
private String HOST = "127.0.0.1";
private final int port = 8821;
private Socket socket;
private String ENV = "linux";// 运行环境
private int bufferSize = 8192;
private Scanner sc = new Scanner(System.in);
/**
* 上传文件
*
* @param req
* @param peer
*/
public void uploadFile(int req, String peer) {
try {
socket = new Socket(HOST, port);
sendMessage(req, peer);// 发送操作类型
System.out.print("输入上传文件的绝对路径:");
String path = sc.next();
// String path = "/home/michael/Desktop/zouning71.rar";
// 发送文件
File file = new File(path);
DataInputStream dis = new DataInputStream(new BufferedInputStream(
new FileInputStream(file)));
DataOutputStream dos = new DataOutputStream(
socket.getOutputStream());
if (!file.exists()) {
System.out.println("该文件不存在");
dos.writeUTF(String.valueOf(Globals.Exit));
dos.flush();
return;
}
sendFile(dis, dos, file);
} catch (Exception e) {
// TODO: handle exception
e.printStackTrace();
}
}
private void sendFile(DataInputStream dis, DataOutputStream dos, File file) {
try {
// 将文件名及长度发给服务器
dos.writeUTF(file.getName());
dos.flush();
dos.writeLong((long) file.length());
dos.flush();
byte[] buf = new byte[bufferSize];
int read = 0;
while (true) {
read = dis.read(buf);
if (read == -1) {
break;
}
dos.write(buf, 0, read);
}
dos.flush();
// 注意关闭socket链接,不然客户端会等待server的数据过来,
// 直到socket超时,导致数据不完整。
dis.close();
dos.close();
} catch (Exception e) {
// TODO: handle exception
e.printStackTrace();
}
}
/**
* 下载文件
*
* @param req
* @param peer
*/
public void downloadFile(int req, String peer) {
try {
socket = new Socket(HOST, port);
sendMessage(req, peer);// 发送操作类型
DataInputStream dis = new DataInputStream(socket.getInputStream());
DataOutputStream dos = new DataOutputStream(
socket.getOutputStream());
// 接收文件列表
String fileList = dis.readUTF();
if (fileList.equals("")) {
System.out.println("服务器没有文件");
return;
}
System.out.println(fileList);
System.out.print("输入要下载的文件序号:");
dos.writeInt(sc.nextInt());
dos.flush();
System.out.print("输入文件的保存位置(绝对路径):");
String savePath = sc.next();
// String savePath = "/home/michael/Desktop/";
recvFile(dis, dos, savePath);
} catch (Exception e) {
// TODO: handle exception
e.printStackTrace();
}
}
private void sendMessage(int req, String peer) {
DataOutputStream dos = null;
try {
dos = new DataOutputStream(socket.getOutputStream());
if (ENV.equalsIgnoreCase("windows")) {
dos.writeByte(0x1);
dos.flush();
} else if (ENV.equalsIgnoreCase("unix")) {
dos.writeByte(0x2);
dos.flush();
} else if (ENV.equalsIgnoreCase("linux")) {
dos.write(0x3);
dos.flush();
} else {
dos.writeUTF(ENV);
dos.flush();
}
dos.writeInt(req);// 向服务器发送操作类型:上传文件OR下载文件
dos.writeUTF(peer);// 上传者或者下载者
dos.flush();
} catch (Exception e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
private void recvFile(DataInputStream dis, DataOutputStream dos,
String savePath) {
try {
byte[] buf = new byte[bufferSize];
long len = 0;
String fileName = dis.readUTF();
if (!savePath.endsWith("/")) {
savePath += "/";
}
savePath += fileName;
dos = new DataOutputStream(new BufferedOutputStream(
new BufferedOutputStream(new FileOutputStream(savePath))));
len = dis.readLong();
System.out.println("文件的长度为:" + len);
int read = 0;
while (true) {
read = dis.read(buf);
if (read == -1) {
break;
}
dos.write(buf, 0, read);
}
System.out.println("接收完成,文件存为" + savePath);
dis.close();
dos.close();
} catch (Exception e) {
e.printStackTrace();
}
}
public static void main(String[] args) {
FileClient client = new FileClient();
client.uploadFile(Globals.UploadReq, "noname");
client.downloadFile(Globals.DownloadReq, "noname");
}
}
上面这两个文件就可以实现文件上传和下载了,有需要完整工程代码的请点击这里。
测试的时候先运行两个服务器:Server和FileServer,然后在运行Main。