Java语言进阶:TCP实现文件上传
TCP实现文件上传案例
需求
- 使用TCP协议, 通过客户端向服务器上传一个文件
分析
-
【客户端】输入流,从硬盘读取文件数据到程序中。
-
【客户端】输出流,写出文件数据到服务端。
-
【服务端】输入流,读取文件数据到服务端程序。
-
【服务端】输出流,写出文件数据到服务器硬盘中。
-
【服务端】获取输出流,回写数据。
-
【客户端】获取输入流,解析回写数据。
实现
拷贝文件
public class Client {
public static void main(String[] args) throws Exception {
// 1.创建输入流对象,关联数据源文件路径
FileInputStream fis = new FileInputStream("day12\\aaa\\hb.jpg");
// 2.创建Socket对象,指定要连接的服务器的ip地址和端口号
Socket socket = new Socket(InetAddress.getLocalHost(), 8888);
// 3.通过Socket对象获得输出流,关联连接通道
OutputStream os = socket.getOutputStream();
// 4.定义变量,用来存储读取到的字节数据
byte[] bys = new byte[8192];
int len;
// 5.循环读取
while ((len = fis.read(bys)) != -1) {
// 6.在循环中,写出数据到通道中
os.write(bys,0,len);
}
// 7.释放资源
socket.close();
fis.close();
}
}
public class Server {
public static void main(String[] args) throws Exception {
// 1.创建ServerSocket对象,指定端口号 8888
ServerSocket ss = new ServerSocket(8888);
// 2.使用ServerSocket对象调用accept()方法,接收请求,建立连接,返回Socket对象
Socket socket = ss.accept();
// 3.通过返回的Socket对象获得输入流,关联连接通道
InputStream is = socket.getInputStream();
// 4.创建输出流对象,关联目的地文件路径
FileOutputStream fos = new FileOutputStream("day12\\aaa\\hbCopy2.jpg");
// 5.定义变量,用来存储读取到的字节数据
byte[] bys = new byte[8192];
int len;
// 6.循环读取
while ((len = is.read(bys)) != -1) {
// 7.在循环中,写出数据目的文件中
fos.write(bys,0,len);
}
// 8.释放资源
fos.close();
socket.close();
}
}
文件上传成功后服务器回写字符串数据
// 客户端
public class Client {
public static void main(String[] args) throws Exception {
// 1.创建输入流对象,关联数据源文件路径
FileInputStream fis = new FileInputStream("day12\\aaa\\hb.jpg");
// 2.创建Socket对象,指定要连接的服务器的ip地址和端口号
Socket socket = new Socket(InetAddress.getLocalHost(), 8888);
// 3.通过Socket对象获得输出流,关联连接通道
OutputStream os = socket.getOutputStream();
// 4.定义变量,用来存储读取到的字节数据
byte[] bys = new byte[8192];
int len;
// 5.循环读取
while ((len = fis.read(bys)) != -1) {
// 6.在循环中,写出数据到通道中
os.write(bys, 0, len);
}
// - 在文件上传时,客户端从文件中读不到数据,就会停止发送。
// 但是服务器端不知道客户端停止了,所以会一直等待接收数据。
// 解决办法:在客户端调用s.shutdownOutput();通知服务器端发送结束了。
socket.shutdownOutput();// 注意
System.out.println("============客户端开始接受服务器返回的数据==============");
// 7.通过Socket对象获取输入流,关联连接通道
InputStream is = socket.getInputStream();
// 8.读取服务器回写的数据
int read = is.read(bys);// 卡死 读取服务器写回的数据,但是服务器又没有写回数据
// 9.打印服务器回写的数据
System.out.println("服务器回写的数据是:" + new String(bys, 0, read));
// 10.释放资源
socket.close();
fis.close();
}
}
// 服务器
public class Server {
public static void main(String[] args) throws Exception {
// 1.创建ServerSocket对象,指定端口号 8888
ServerSocket ss = new ServerSocket(8888);
// 2.使用ServerSocket对象调用accept()方法,接收请求,建立连接,返回Socket对象
Socket socket = ss.accept();
// 3.通过返回的Socket对象获得输入流,关联连接通道
InputStream is = socket.getInputStream();
// 4.创建输出流对象,关联目的地文件路径
FileOutputStream fos = new FileOutputStream("day12\\aaa\\hbCopy5.jpg");
// 5.定义变量,用来存储读取到的字节数据
byte[] bys = new byte[8192];
int len;
// 6.循环读取
while ((len = is.read(bys)) != -1) {
// 7.在循环中,写出数据目的文件中
fos.write(bys, 0, len);
}
System.out.println("======服务器开始回写数据给客户端=======");
// 7.通过socket对象获取输出流,关联连接通道
OutputStream os = socket.getOutputStream();
// 8.写出数据到通道中
os.write("恭喜您,上传成功!".getBytes());
// 9.释放资源
fos.close();
socket.close();
}
}
优化文件上传案例
1.文件名固定----->优化 自动生成唯一的文件名
2.服务器只能接受一次 ----> 优化 死循环去接收请求,建立连接
3.例如:如果张三先和服务器建立连接,上传了一个2GB字节大小的文件
- 李四后和服务器建立连接,上传了一个2MB字节大小的文件
- 李四就必须等张三上传完毕,才能上传文件
优化---->多线程优化
张三上传文件,开辟一条线程
李四上传文件,开辟一条线程
代码块:
// 服务器
public class Server {
// year+"年"+month+"月"+day+"日"+....+"毫秒"
public static void main(String[] args) throws Exception {
// 1.创建ServerSocket对象,指定端口号 8888
ServerSocket ss = new ServerSocket(8888);
while (true) {
// 2.使用ServerSocket对象调用accept()方法,接收请求,建立连接,返回Socket对象
Socket socket = ss.accept();
new Thread(new Runnable() {
@Override
public void run() {
try {
// 3.通过返回的Socket对象获得输入流,关联连接通道
InputStream is = socket.getInputStream();
// 4.创建输出流对象,关联目的地文件路径
FileOutputStream fos = new FileOutputStream("day12\\aaa\\"+System.currentTimeMillis()+".jpg");
// 5.定义变量,用来存储读取到的字节数据
byte[] bys = new byte[8192];
int len;
// 6.循环读取
while ((len = is.read(bys)) != -1) {
// 7.在循环中,写出数据目的文件中
fos.write(bys, 0, len);
}
System.out.println("======服务器开始回写数据给客户端=======");
// 7.通过socket对象获取输出流,关联连接通道
OutputStream os = socket.getOutputStream();
// 8.写出数据到通道中
os.write("恭喜您,上传成功!".getBytes());
// 9.释放资源
fos.close();
socket.close();
} catch (IOException e) {
}
}
}).start();
}
}
}
// 客户端
public class Client {
public static void main(String[] args) throws Exception {
// 1.创建输入流对象,关联数据源文件路径
FileInputStream fis = new FileInputStream("day12\\aaa\\hb.jpg");
// 2.创建Socket对象,指定要连接的服务器的ip地址和端口号
Socket socket = new Socket(InetAddress.getLocalHost(), 8888);
// 3.通过Socket对象获得输出流,关联连接通道
OutputStream os = socket.getOutputStream();
// 4.定义变量,用来存储读取到的字节数据
byte[] bys = new byte[8192];
int len;
// 5.循环读取
while ((len = fis.read(bys)) != -1) {
// 6.在循环中,写出数据到通道中
os.write(bys, 0, len);
}
// 想办法,告诉服务器,我客户端写完了数据,我再也不会写数据了
socket.shutdownOutput();// 注意
System.out.println("============客户端开始接受服务器返回的数据==============");
// 7.通过Socket对象获取输入流,关联连接通道
InputStream is = socket.getInputStream();
// 8.读取服务器回写的数据
int read = is.read(bys);
// 9.打印服务器回写的数据
System.out.println("服务器回写的数据是:" + new String(bys, 0, read));
// 10.释放资源
socket.close();
fis.close();
}
}