第三章、綜合案例
3.1 文件上傳案例
原理:客戶端讀取本地的文件,把文件上傳到服務器,服務器把上傳的文件保存到服務器的硬盤上
-
客戶端使用本地的字節輸入流,讀取要上傳的文件
-
客戶端使用網絡字節輸出流,把讀取到的文件上傳到服務器
-
服務器使用網絡字節輸入流,讀取客戶端上傳的文件
-
服務器使用本地的字節輸出流,把讀取到的文件保存到服務器的硬盤上
-
服務器使用網絡字節輸出流,給客戶端回寫一個“上傳成功”
-
客戶端使用網絡字節輸入流讀取服務器回寫的數據
-
釋放資源
注意:
客戶端和服務器和本地硬盤進行讀寫,需要使用自己創建的字節流對象(本地流)
客戶端和服務器之間進行讀寫,必須使用Socket中提供的字節流對象(網絡流)
文件上傳的原理,就是文件的複製
明確:
數據源
數據目的地
文件上傳優化
-
文件名稱寫死:創建文件使用不同名字
-
循環接受問題:始終開啓Server,不斷接受文件
-
效率問題:多線程解決
服務器代碼:
/**
* 文件上傳案例服務器:讀取客戶端上傳的文件,保存到服務器的硬盤,給客戶端回寫"上傳成功"
*/
public class TCPServer {
public static void main(String[] args) throws IOException {
//1、創建一個服務器ServerSocket對象,和系統要指定的端口號
ServerSocket server = new ServerSocket(8888);
//2、使用ServerSocket對象中的方法accept獲取到請求的客戶端Socket對象
/*
* 讓服務器一直處於監聽狀態
*/
while (true){
Socket socket = server.accept();
/*
* 使用多線程,上傳一次文件創建一個線程
*/
new Thread(new Runnable() {
@Override
public void run() {
try{
//3、使用Socket對象中的方法getInputSteam,獲取到網絡字節輸入流InputSteam對象
InputStream is = socket.getInputStream();
//4、判斷服務器硬盤上upload文件夾是否存在,不存在則創建
File file = new File("/Users/cwq/IdeaProjects/Net/src/upload");
if(!file.exists()){
file.mkdir();
}
//5、創建本地字節輸出流對象FileOutputStream,構造方法中綁定要輸出的目的地
/*
* 自定義命名規則:域名+毫秒值+隨機數
jpg文件前需要加/
*/
//FileOutputStream fos = new FileOutputStream(file + "/1.jpg");
String filename = "cwq" + System.currentTimeMillis() + new Random().nextInt() + ".jpg";
FileOutputStream fos = new FileOutputStream(file + "/" + filename);
//6、使用網絡字節輸入流InputSteam的方法read讀取客戶端上傳的文件
int len = 0;
byte[] bytes = new byte[1024];
while((len = is.read(bytes))!=-1){
//7、使用本地字節輸出流對象中的方法write把讀取到的文件保存到服務器的硬盤上
fos.write(bytes,0,len);
}
//8、使用Socket對象中的方法getOutputSteam,獲取到網絡字節輸出流OutputSteam對象
//9、使用網絡字節輸出流OutputSteam對象的write方法給客戶端回寫"上傳成功"
socket.getOutputStream().write("上傳成功".getBytes());
//10、釋放資源(FileOutputStream,Socket,ServerSocket)
fos.close();
socket.close();
}catch (IOException e){
System.out.println(e);
}
}
}).start();
}
//server.close();
}
}
客戶端代碼
/**
* 文件上傳案例的客戶端:讀取本地文件,上傳到服務器,讀取服務器回寫的數據
*/
public class TCPClient {
public static void main(String[] args) throws IOException {
//1、創建一個本地字節輸入流FileInputStream對象,構造方法中綁定要讀取的數據源
FileInputStream fis = new FileInputStream("/Users/cwq/IdeaProjects/Net/src/image/1.jpg");
//2、創建一個客戶端Socket對象,構造方法中綁定服務器的IP地址和端口號
Socket socket = new Socket("127.0.0.1",8888);
//3、使用Socket中的方法getOutputStream,獲取網絡字節輸出流OutputStream對象
OutputStream os = socket.getOutputStream();
//4、使用本地字節輸入流FileInputStream對象中方法read,讀取本地文件
int len = 0;
byte[] bytes = new byte[1024];
while ((len = fis.read(bytes))!=-1){
//5、使用網絡字節輸出流OutputStream對象中的方法write,把讀取到的文件上傳到服務器
os.write(bytes,0,len);
}
/**
* read方法讀取不到-1。導致阻塞,while進入死循環
* 解決:上傳完文件,給服務器一個結束標記
* void shutdownOutput() 禁止此套接字的輸出流
* 對於TCP套接字,任何以前寫入的數據都將被髮送,並且後跟TCP的正常連接終止序列
*/
socket.shutdownOutput();
//6、使用Socket中的方法getInputStream,獲取網絡字節輸入流InputStream對象
InputStream is = socket.getInputStream();
//7、使用網絡字節輸入流InputStream對象的方法read讀取服務器回寫的數據
while ((len = is.read(bytes))!=-1){
System.out.println(new String(bytes,0,len));
}
//8、釋放資源(FileInputStream,Socket)
fis.close();
socket.close();
}
}