Java - Use the MD5 checksum to confirm that the downloaded file is correct

本篇主旨在於client端下載server端的檔案後,進行MD5 checksum的檢查,確認檔案是否無誤!
程式撰寫如下:
1. 建立Server.java,等待Client端連線,連線上之後呈列目前可供下載的檔案
2. 建立Client.java,與Server端連線後,選擇檔案後進行下載
3. 建立Main.java,代表程式入口,可輸入相關參數選擇要執行哪一種模式(Server or Client)

#Server.java
  1. package service;
  2. import java.io.File;
  3. import java.io.FileInputStream;
  4. import java.io.IOException;
  5. import java.io.InputStream;
  6. import java.io.ObjectInputStream;
  7. import java.io.ObjectOutputStream;
  8. import java.net.ServerSocket;
  9. import java.net.Socket;
  10. import main.Main;
  11. public class Server {
  12. private ServerSocket server;
  13. private File sharePath;
  14. public Server(int port, File sharePath) throws IOException {
  15. this.sharePath = sharePath;
  16. this.server = new ServerSocket(port);
  17. }
  18. public void startService() {
  19. while(!server.isClosed()) {
  20. try {
  21. System.out.println("Waiting for the Client connection...");
  22. Socket socket = server.accept();
  23. String client = socket.getInetAddress().getHostAddress();
  24. System.out.println("Connection received from: "+client);
  25. ObjectOutputStream oos = new ObjectOutputStream(socket.getOutputStream());
  26. ObjectInputStream ois = new ObjectInputStream(socket.getInputStream());
  27. File listFiles[] = sharePath.listFiles();
  28. oos.writeObject("The files that can be downloaded are as follows:");
  29. int index = 0;
  30. oos.writeObject(listFiles.length);
  31. for(File f : listFiles) {
  32. oos.writeObject("("+(++index)+")"+f.getName());
  33. }
  34. while (!socket.isClosed()) {
  35. System.out.println("Waiting for the "+client+" to send option id");
  36. Object selection = ois.readObject();
  37. if(selection instanceof Integer) {
  38. int sel = (int) selection;
  39. System.out.println("Client send option id is "+sel);
  40. if(sel == 0) {
  41. socket.close();
  42. break;
  43. }
  44. if(sel > listFiles.length) {
  45. System.out.println("File non-exist!!");
  46. oos.writeLong(-1L);
  47. oos.flush();
  48. continue;
  49. }
  50. File sendFile = listFiles[sel - 1];
  51. oos.writeLong(sendFile.length());
  52. oos.writeObject(sendFile.getName());
  53. String md5 = Main.getCheckSumByMD5(sendFile.getAbsolutePath());
  54. oos.writeObject(md5);
  55. InputStream is = new FileInputStream(sendFile);
  56. int read;
  57. byte[] buf = new byte[1024];
  58. while((read = is.read(buf, 0, buf.length)) != -1) {
  59. oos.write(buf, 0, read);
  60. oos.flush();
  61. }
  62. is.close();
  63. System.out.println("File "+sendFile.getName()+" transfer completed!");
  64. System.out.println("===========================");
  65. }
  66. }
  67. System.out.println("Client "+client+" disconnected!\n");
  68. ois.close();
  69. oos.close();
  70. }catch (IOException | ClassNotFoundException e) {
  71. e.printStackTrace();
  72. terminateService();
  73. }
  74. }
  75. }
  76. public void terminateService() {
  77. try {
  78. this.server.close();
  79. System.out.println("Shutdown server");
  80. } catch (IOException e) {
  81. // TODO Auto-generated catch block
  82. e.printStackTrace();
  83. }
  84. }
  85. }

client連上後,server傳送檔案清單,當收到項目的編號後,依序將檔案size(bytes)、名稱、md5 checksum傳給client,接下來就是data部分,buffer設定1024 bytes分批傳送! 傳送完畢後等待client下一個項目編號!
PS. 在這邊只示範傳送檔案,若是目錄不在測試範圍內

#Client.java
  1. package service;
  2. import java.io.File;
  3. import java.io.FileOutputStream;
  4. import java.io.IOException;
  5. import java.io.ObjectInputStream;
  6. import java.io.ObjectOutputStream;
  7. import java.net.InetAddress;
  8. import java.net.Socket;
  9. import java.net.UnknownHostException;
  10. import java.util.Scanner;
  11. import main.Main;
  12. public class Client {
  13. private File dPath;
  14. public Client(File downloadPath) {
  15. this.dPath = downloadPath;
  16. }
  17. public void startService() {
  18. InetAddress host;
  19. Scanner input = new Scanner(System.in);
  20. try {
  21. host = InetAddress.getLocalHost();
  22. Socket socket = new Socket(host.getHostName(), 5880);
  23. ObjectOutputStream oos = new ObjectOutputStream(socket.getOutputStream());
  24. ObjectInputStream ois = new ObjectInputStream(socket.getInputStream());
  25. System.out.println(ois.readObject());
  26. int num = (int) ois.readObject();
  27. int i = 0;
  28. while(i++ < num) {
  29. System.out.println(ois.readObject());
  30. }
  31. while(!socket.isClosed()) {
  32. System.out.println("Waiting to send the option id [1 ~ "+num+"] to download file from server...");
  33. System.out.println("Note. Terminate connection please input option 0");
  34. int option = input.nextInt();
  35. oos.writeObject(option);
  36. if(option == 0) {
  37. socket.close();
  38. System.exit(-1);
  39. }
  40. System.out.println("Send option id is "+option);
  41. long totalByte = ois.readLong();
  42. if(totalByte == -1L) {
  43. System.out.println("Download file non-exist!!");
  44. System.out.println("===========================");
  45. continue;
  46. }
  47. System.out.println("Download file total size is "+totalByte+" bytes");
  48. String filename = (String) ois.readObject();
  49. String checksum = (String) ois.readObject();
  50. String path = dPath.getAbsolutePath() + File.separator + filename;
  51. System.out.println("Download to "+path);
  52. byte[] buffer = new byte[1024];
  53. long cur = 0L;
  54. FileOutputStream fos = new FileOutputStream(path);
  55. while(cur < totalByte) {
  56. int len = ois.read(buffer);
  57. cur += len;
  58. fos.write(buffer, 0, len);
  59. double percent = ((double)cur / totalByte) * 100;
  60. System.out.println("Download current size = "+cur + " bytes, percent = "+percent+"%");
  61. }
  62. System.out.println("Download completed!!\n");
  63. fos.close();
  64. System.out.println("Check if the file is correct>>>");
  65. String md5 = Main.getCheckSumByMD5(path);
  66. System.out.println("Transferred file checksum => "+checksum);
  67. System.out.println("Downloaded file checksum => "+md5);
  68. if(md5.equals(checksum)) {
  69. System.out.println("Yes. It is corrent!");
  70. }else {
  71. System.out.println("No. It is incorrent!");
  72. }
  73. System.out.println("===========================");
  74. }
  75. input.close();
  76. ois.close();
  77. oos.close();
  78. } catch (UnknownHostException e) {
  79. // TODO Auto-generated catch block
  80. e.printStackTrace();
  81. } catch (IOException e) {
  82. // TODO Auto-generated catch block
  83. e.printStackTrace();
  84. } catch (ClassNotFoundException e) {
  85. // TODO Auto-generated catch block
  86. e.printStackTrace();
  87. }
  88. }
  89. }

client端連上server後,收到檔案清單,傳送項目編號後,收到檔案大小判斷是否存在,開始接收data後,計算當下下載進度,完畢後計算檔案md5 checksum,與server端的checksum進行比對,確認是否檔案傳輸正確!

#Main.java
  1. package main;
  2. import java.io.File;
  3. import java.io.IOException;
  4. import java.io.InputStream;
  5. import java.nio.file.Files;
  6. import java.nio.file.Paths;
  7. import org.apache.commons.codec.digest.DigestUtils;
  8. import service.Client;
  9. import service.Server;
  10. public class Main {
  11. private static final int PORT = 5880;
  12. public static void main(String[] args) {
  13. // TODO Auto-generated method stub
  14. if(args.length == 0) {
  15. System.out.println("<<File Transfer Service>>"
  16. + "\nStart client mode -> java -jar ftransfer.jar client [target path]"
  17. + "\nStart server mode -> java -jar ftransfer.jar server [share path]");
  18. }else {
  19. if(args.length == 1) {
  20. System.out.println("Please specify path");
  21. }else {
  22. File path = new File(args[1]);
  23. if(!path.exists()) {
  24. System.out.println("The path non-exist");
  25. }else {
  26. String mode = args[0];
  27. if(mode.equals("server")) {
  28. Server server = null;
  29. try {
  30. server = new Server(PORT, path);
  31. server.startService();
  32. } catch (IOException e) {
  33. // TODO Auto-generated catch block
  34. e.printStackTrace();
  35. } finally {
  36. if(server != null) {
  37. server.terminateService();
  38. }
  39. }
  40. }else if(mode.equals("client")) {
  41. Client client = new Client(path);
  42. client.startService();
  43. }
  44. }
  45. }
  46. }
  47. }
  48. public static String getCheckSumByMD5(String filepath) throws IOException {
  49. try (InputStream is = Files.newInputStream(Paths.get(filepath))) {
  50. return DigestUtils.md5Hex(is);
  51. }
  52. }
  53. }

依據主程式輸入的參數來決定開啟server or client mode

最後DEMO如下:
1. 透過eclipse輸出成可執行的jar檔FileTransfer.jar進行示範
2. 執行java -jar FileTransfer.jar (展現提示)
3. 啟動server,指定分享路徑D:\server
4. 啟動client,指定下載存放目錄E:\
5. server端輸出client端資訊,並且等待項目編號的回應
6. client端下載項目1
7. server端顯示收到需傳送項目1的檔案,傳送完畢後等待下一個項目要求
8. client端輸入無效id & terminate server connection
9. server端顯示檔案不存在 & terminate client connection
上面的步驟對照下面的圖示如下

留言