Java基础进阶笔记 - Day11 - 第三章 综合案例
- Java基础进阶笔记 - Day11 - 第三章 综合案例
-
- 3.1 文件上传案例
- 3.2 模拟BS服务器
Java基础进阶笔记 - Day11 - 第三章 综合案例
系统:Win10
JDK:1.8.0_121
IDE:IntelliJ IDEA 2017.3.7
3.1 文件上传案例
文件上传分析图解
- 【客户端】输入流,从硬盘读取文件数据到程序中
- 【客户端】输出流,写出文件数据到服务端
- 【服务端】输入流,读取文件数据到服务端程序
- 【服务端】输出流,写出文件数据到服务器硬盘中。
![](https://img.laitimes.com/img/9ZDMuAjOiMmIsIjOiQnIsIyZuBnLwIzN4QDOxAjM1AzNwEjMwIzLc52YucWbp5GZzNmLn9Gbi1yZtl2Lc9CX6MHc0RHaiojIsJye.png)
基本实现
服务端实现:
public class FileUploadServer {
public static void main(String[] args) throws IOException {
System.out.println("服务器启动...");
// 1.创建服务端ServerSocket
ServerSocket server = new ServerSocket(6666);
// 2.建立连接
Socket socket = server.accept();
// 3.创建流对象
// 3.1获取输入流,读取文件数据
BufferedInputStream bis = new BufferedInputStream(socket.getInputStream());
// 3.2创建输出流,保存到本地
BufferedOutputStream bos = new BufferedOutputStream(new FileOutputStream("D:\\output.png"));
// 4.读写数据
int len = 0;
byte[] bytes = new byte[1024];
while ((len = bis.read(bytes)) != -1) {
bos.write(bytes, 0, len);
}
// 5.关闭资源
bos.close();
bis.close();
server.close();
System.out.println("文件上传成功!!!");
}
}
客户端实现:
public class FileUploadClient {
public static void main(String[] args) throws IOException {
// 1.创建流对象
BufferedInputStream bis = new BufferedInputStream(new FileInputStream("C:\\1.png"));
// 2.创建输出流,写到服务器
Socket socket = new Socket("127.0.0.1", 6666);
BufferedOutputStream bos = new BufferedOutputStream(socket.getOutputStream());
// 3.写数据
byte[] bytes = new byte[1024];
int len = 0;
while ((len = bis.read(bytes)) != -1) {
bos.write(bytes,0,len);
}
System.out.println("文件发送完毕!");
// 4.释放资源
bos.close();
socket.close();
bis.close();
}
}
文件上传优化分析
-
文件名称写死的问题
服务端,保存文件的名称如果写死,那么最终导致服务器硬盘,只会保留一个文件,建议使用系统时间优化,保证文件名称唯一,代码如下:
// 3.2创建输出流,保存到本地
BufferedOutputStream bos = new BufferedOutputStream(new FileOutputStream("D:\\" + System.currentTimeMillis() + ".png"));
-
循环接收的问题
服务端,指保存一个文件就关闭了,之后的用户无法再上传,这是不符合实际的,使用循环改进,可以不断的接收不同用户的文件,代码如下:
// 每次接收新的连接,创建一个Socket
while(true){
Socket socket = server.accept();
......
}
-
效率问题
服务端,在接收大文件时,可能耗费几秒钟的时间,此时不能接收其他用户上传,所以,使用多线程技术优化,代码如下:
while(true){
Socket socket = server.accept();
// socket 交予子线程处理
new Thread(new Runnable() {
@Override
public void run() {
......
}).start();
}
优化实现
public class FileUploadServer {
public static void main(String[] args) throws IOException {
System.out.println("服务器启动...");
// 1.创建服务端ServerSocket
ServerSocket server = new ServerSocket(6666);
while (true) {
// 2.建立连接
Socket socket = server.accept();
new Thread(new Runnable() {
@Override
public void run() {
try {
// 3.创建流对象
// 3.1获取输入流,读取文件数据
BufferedInputStream bis = new BufferedInputStream(socket.getInputStream());
// 3.2创建输出流,保存到本地
BufferedOutputStream bos = new BufferedOutputStream(new FileOutputStream("D:\\" + System.currentTimeMillis() + ".png"));
// 4.读写数据
int len = 0;
byte[] bytes = new byte[10240];
while ((len = bis.read(bytes)) != -1) {
bos.write(bytes, 0, len);
}
// 5.关闭资源
bos.close();
bis.close();
socket.close();
System.out.println("文件上传成功!!!");
} catch (IOException e) {
e.printStackTrace();
}
}
}).start();
}
}
}
信息回写分析图解
前四步与基本文件上传一致
- 【服务端】获取输出流,回写数据
- 【客户端】获取输入流,解析回写数据。
回写实现
public class FileUploadServer {
public static void main(String[] args) throws IOException {
System.out.println("服务器启动...");
// 1.创建服务端ServerSocket
ServerSocket server = new ServerSocket(6666);
while (true) {
// 2.建立连接
Socket socket = server.accept();
new Thread(new Runnable() {
@Override
public void run() {
try {
// 3.创建流对象
// 3.1获取输入流,读取文件数据
BufferedInputStream bis = new BufferedInputStream(socket.getInputStream());
// 3.2创建输出流,保存到本地
BufferedOutputStream bos = new BufferedOutputStream(new FileOutputStream("D:\\" + System.currentTimeMillis() + ".png"));
// 4.读写数据
int len = 0;
byte[] bytes = new byte[10240];
while ((len = bis.read(bytes)) != -1) {
bos.write(bytes, 0, len);
}
System.out.println("文件上传成功!!!");
/* 信息回写 */
System.out.println("信息回写...");
OutputStream os = socket.getOutputStream();
os.write("回写上传成功!".getBytes());
os.close();
// 5.关闭资源
bos.close();
bis.close();
socket.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}).start();
}
}
}
客户端实现:
public class FileUploadClient {
public static void main(String[] args) throws IOException {
// 1.创建流对象
BufferedInputStream bis = new BufferedInputStream(new FileInputStream("C:\\1.png"));
// 2.创建输出流,写到服务器
Socket socket = new Socket("127.0.0.1", 6666);
BufferedOutputStream bos = new BufferedOutputStream(socket.getOutputStream());
// 3.写数据
byte[] bytes = new byte[10240];
int len = 0;
while ((len = bis.read(bytes)) != -1) {
bos.write(bytes,0,len);
}
// 4.关闭输出流,通知客户端,写数据完毕
socket.shutdownOutput();
System.out.println("文件发送完毕!");
// 5.解析回写
InputStream is = socket.getInputStream();
byte[] back = new byte[1024];
is.read(back);
System.out.println(new String(back));
is.close();
// 6.释放资源
socket.close();
bis.close();
}
}
3.2 模拟BS服务器
模拟网站服务器,使用浏览器访问自己编写的服务端程序,查看网页效果。
案例分析
- 准备页面数据,web文件夹。复制到我们Module中,比如复制到BSDemo中
【Java基础进阶笔记】- Day11 - 第三章 综合案例Java基础进阶笔记 - Day11 - 第三章 综合案例 - 我们模拟服务器端,ServerSocket类监听端口,使用浏览器访问
public class BSServer {
public static void main(String[] args) throws IOException {
ServerSocket server = new ServerSocket(8888);
Socket socket = server.accept();
InputStream is = socket.getInputStream();
byte[] bytes = new byte[1024];
int len = is.read(bytes);
System.out.println(new String(bytes));
socket.close();
server.close();
}
}
- 服务器程序中字节输入流可以读取到浏览器发来的请求信息
【Java基础进阶笔记】- Day11 - 第三章 综合案例Java基础进阶笔记 - Day11 - 第三章 综合案例
GET /BSDemo/web/index.html HTTP/1.1
Host: 127.0.0.1:8888
Connection: keep-alive
Cache-Control: max-age=0
sec-ch-ua: " Not;A Brand";v="99", "Google Chrome";v="91", "Chromium";v="91"
sec-ch-ua-mobile: ?0
Upgrade-Insecure-Requests: 1
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/91.0.4472.77 Safari/537.36
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.9
Sec-Fetch-Site: none
Sec-Fetch-Mode: navigate
Sec-Fetch-User: ?1
Sec-Fetch-Dest: document
Accept-Encoding: gzip, deflate, br
Accept-Language: zh-CN,zh;q=0.9
GET /BSDemo/web/index.html HTTP/1.1 是浏览器的请求消息。/BSDemo/web/index.html 为浏览器想要请求的服务器端的资源,使用字符串切割方式获取到请求的资源。
// 转换流,读取浏览器请求的第一行数据
BufferedReader br = new BufferedReader(new InputStreamReader(socket.getInputStream()));
String line = br.readLine();
// 获取请求资源的路径
String[] arr = line.split(" ");
String path = arr[1].substring(1);
System.out.println(path);
案例实现
服务端实现:
public class BSServer {
public static void main(String[] args) throws IOException {
ServerSocket server = new ServerSocket(8888);
Socket socket = server.accept();
// 转换流,读取浏览器请求的第一行数据
BufferedReader br = new BufferedReader(new InputStreamReader(socket.getInputStream()));
String line = br.readLine();
// 获取请求资源的路径
String[] arr = line.split(" ");
String path = arr[1].substring(1);
// 读取客户端请求的资源文件
FileInputStream fis = new FileInputStream(path);
byte[] bytes = new byte[1024];
int len = 0;
// 字节输出流,将文件回写客户端
OutputStream os = socket.getOutputStream();
// 写入HTTP协议响应头,固定写法
os.write("HTTP/1.1 200 OK\r\n".getBytes());
os.write("Content-Type:text/html\r\n".getBytes());
// 必须要写入空行,否则浏览器不会解析,解析了也会乱码
os.write("\r\n".getBytes());
while ((len = fis.read(bytes)) != -1) {
os.write(bytes, 0, len);
}
// 关闭资源
os.close();
fis.close();
br.close();
socket.close();
server.close();
}
}
访问效果-chrome
小贴士:不同的浏览器,内核不一样,解析效果有可能不一样。
发现浏览器中出现很多的叉子,说明浏览器没有读取到图片信息导致。 浏览器工作原理是遇到图片会开启一个线程进行单独的访问,因此在服务器端加入线程技术。
public class BSServer {
public static void main(String[] args) throws IOException {
ServerSocket server = new ServerSocket(8888);
while(true){
Socket socket = server.accept();
new Thread(new Runnable() {
@Override
public void run() {
try{
// 转换流,读取浏览器请求的第一行数据
BufferedReader br = new BufferedReader(new InputStreamReader(socket.getInputStream()));
String line = br.readLine();
// 获取请求资源的路径
String[] arr = line.split(" ");
String path = arr[1].substring(1);
// 读取客户端请求的资源文件
FileInputStream fis = new FileInputStream(path);
byte[] bytes = new byte[1024];
int len = 0;
// 字节输出流,将文件回写客户端
OutputStream os = socket.getOutputStream();
// 写入HTTP协议响应头,固定写法
os.write("HTTP/1.1 200 OK\r\n".getBytes());
os.write("Content-Type:text/html\r\n".getBytes());
// 必须要写入空行,否则浏览器不会解析,解析了也会乱码
os.write("\r\n".getBytes());
while ((len = fis.read(bytes)) != -1) {
os.write(bytes, 0, len);
}
// 关闭资源
os.close();
fis.close();
br.close();
socket.close();
} catch (IOException e){
e.printStackTrace();
}
}
}).start();
}
}
}
访问效果:
BS通信图解: