今天做文件上傳功能,需求要求文件內容相同的不能重復上傳。感覺這個需求挺簡單的就交給了一位剛入行的新同學。等合並代碼的時候發現這位同學居然用文件名稱相同和文件大小相同作為兩個文件相同的依據。這種條件判斷靠譜嗎?
從概率上來說遇到兩個文件名稱和大小都一樣的概率確實太小了。這種判斷放在生產環境中也可以穩定的跑上一陣子,不過即使再低的可能性也是有可能的,如果能做到100%就好了。
文件摘要校驗
我相信同學們都下載過一些好心人開發的小工具,有些小工具會附帶一個校驗器讓你校驗附帶提供的checksum值,防止有人惡意篡改小工具,保證小工具可以放心使用。
如果兩個文件的內容相同,那么它們的摘要應該是相同的。這個原理能不能幫助我們鑒定兩個文件是否相同呢?
Java實現文件摘要
帶着這個疑問,我寫了一個文件摘要提取工具類:
/** * 提取文件 checksum * * @param path 文件全路徑 * @param algorithm 算法名 例如 MD5、SHA-1、SHA-256等 * @return checksum * @throws NoSuchAlgorithmException the no such algorithm exception * @throws IOException the io exception */ public static String extractChecksum(String path, String algorithm) throws NoSuchAlgorithmException, IOException { // 根據算法名稱初始化摘要算法 MessageDigest digest = MessageDigest.getInstance(algorithm); // 讀取文件的所有比特 byte[] fileBytes = Files.readAllBytes(Paths.get(path)); // 摘要更新 digest.update(fileBytes); //完成哈希摘要計算並返回特征值 byte[] digested = digest.digest(); // 進行十六進制的輸出 return HexUtils.toHexString(digested); }
接下來做幾組對照試驗來證明猜想。
內容不變
首先要證明一個文件在內容不變的情況下摘要是否有變化,多次執行下面的代碼,斷言始終都是true
。
String path = "C:\\Users\\s1\\IdeaProjects\\demo\\src\\main\\resources\\application.yml"; String checksum = extractChecksum(path, "SHA-1"); String hash = "6bf4d6c101b4a7821226d3ec1f8d778a531bf265"; Assertions.assertEquals(hash,checksum);
而且我把文件名改成application-dev.yml
,甚至application-dev.txt
摘要都是相同的。我又把yml
文件的內容作了改動,斷言就false
了。這證明了單個文件的情況下,內容不變,hash是不變的。
文件復制
我把yml
文件復制了一份,改了文件名稱和類型,不改變內容並存到了另一個目錄中,來測試一下它們的摘要是否有變化。
String path1 = "C:\\Users\\s1\\IdeaProjects\\demo\\src\\main\\resources\\application.yml"; String path2 = "C:\\Users\\s1\\IdeaProjects\\demo\\src\\main\\resources\\templates\\application-dev.txt"; String checksum1 = extractChecksum(path1, "SHA-1"); String checksum2 = extractChecksum(path2, "SHA-1"); String hash = "6bf4d6c101b4a7821226d3ec1f8d778a531bf265"; Assertions.assertEquals(hash,checksum1); Assertions.assertEquals(hash,checksum2);
結果斷言通過,不過改變了其中一個文件的內容后斷言就不通過了。
新建空文件
這里的新建空文件指的是沒有進行任何操作的新建的空文件。
新建的空文件會根據特定的算法返回一個固定值,比如SHA-1
算法下的空文件值是:
da39a3ee5e6b4b0d3255bfef95601890afd80709
結論
通過實驗證明了:
- 在相同算法下,任何新建空文件的摘要值都是固定的。
- 任何兩個內容相同的文件的摘要值都是相同的,和路徑、文件名、文件類型無關。
- 文件的摘要值會隨着文件內容的改變而改變。
文件摘要運用
根據上面的結論,文件摘要是可以防止同樣內容的文件重復提交的, 存儲的時候不但要存儲文件的路徑,還要存儲文件的摘要值,可能需要注意新建空文件的的固定摘要問題。另外在Java12中提供了新的API來處理文件內容重復問題,有興趣的可以研究一下。文件摘要除了防篡改和去重之外,你知道還有其它什么用途嗎?歡迎同學們留言討論。