C/S+P2P网络模型(二)--上传&下载文件

上一篇文章我们实现聊天的功能,下面我们看文件传输怎么实现。

我的做法是:增加一个文件服务器,所有上传和下载文件的操作都由文件服务器来处理。

因此处理逻辑是这样的:如果用户请求上传文件或者下载文件,那么就将用户直接与文件服务器通信,而不用经过中央服务器。

所以现在的问题是知道java怎么实现上传和下载文件,如果这个问题解决了,那基本就搞定了。

首先,文件传输基本都是用面向连接的方式因为无连接的方式容易丢包,一旦丢了一个数据包,文件就坏了,所有努力全白费。但是需要注意的是面向连接的方式,在服务器处理完一个连接后该连接就关闭了。

下面看代码,在原来的基础上我新建了两个主要文件:FileServer.javaFileClient..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。

  • 0
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值