RAR檔案格式由WinRAR開發,廣泛用於檔案壓縮和歸檔。隨著技術的發展,RAR5作為更新的版本,引入了多項改進以提高壓縮效率和資料安全性。
- 壓縮效率:RAR5透過增大字典大小至32MB,相較於RAR4的4MB,能夠更有效地找到資料中的重複模式,從而提高壓縮率,特別是在處理大型檔案時。
- 安全性增強:RAR5採用的256位AES加密演算法,提供了更高階別的資料保護,相較於RAR4的加密標準,更難被破解。
- 時間戳的國際化:RAR5使用UTC時間,解決了RAR4使用本地時間可能導致的時區混淆問題,使得檔案的時間戳在全球範圍內保持一致性。
- 相容性考慮:RAR5的格式較新,可能不被舊版本的解壓軟體識別。在需要確保最大相容性的場景下,可能仍需使用RAR4格式。
- 恢復卷的改進:RAR5格式支援的恢復卷數量大大增加,從RAR4的255個增加到65535個,這在處理多卷壓縮檔案時提供了更高的靈活性和容錯性。
- 錯誤糾正能力:RAR5的恢復記錄基於Reed-Solomon錯誤糾正碼,顯著提高了壓縮檔案在受損情況下的自我修復能力。
- 日誌檔案編碼:RAR5使用UTF-16小端位元組序編碼,確保了日誌檔案中Unicode字元的正確儲存和顯示,提高了對國際化檔名的支援。
RAR5的Java解壓實現
在Java中實現RAR5檔案的解壓,可以藉助java-unrar
和SevenZipJBinding
庫。以下是具體的實現步驟和程式碼示例。
1、新增依賴:在專案的pom.xml
檔案中新增相關依賴。
<dependency> <groupId>com.github.axet</groupId> <artifactId>java-unrar</artifactId> <version>1.7.0-8</version> </dependency> <dependency> <groupId>net.sf.sevenzipjbinding</groupId> <artifactId>sevenzipjbinding</artifactId> <version>16.02-2.01</version> </dependency> <dependency> <groupId>net.sf.sevenzipjbinding</groupId> <artifactId>sevenzipjbinding-all-platforms</artifactId> <version>16.02-2.01</version> </dependency>
2、編寫解壓工具類:建立Rar5DocExtractor
類,實現解壓邏輯。
1 package rar5; 2 3 import net.sf.sevenzipjbinding.*; 4 import net.sf.sevenzipjbinding.impl.RandomAccessFileInStream; 5 6 import java.io.*; 7 import java.util.*; 8 9 public class Rar5DocExtractor { 10 11 public List<File> extractFiles(File rarFile, File outputDir) throws IOException { 12 Set<File> extractedFiles = new HashSet<>(); 13 if (!outputDir.exists()) { 14 outputDir.mkdirs(); // 確保輸出目錄存在 15 } 16 17 RandomAccessFile randomAccessFile = null; 18 IInArchive inArchive = null; 19 try { 20 randomAccessFile = new RandomAccessFile(rarFile, "r"); 21 inArchive = SevenZip.openInArchive(null, new RandomAccessFileInStream(randomAccessFile)); 22 int[] in = new int[inArchive.getNumberOfItems()]; 23 for (int i = 0; i < in.length; i++) { 24 in[i] = i; 25 } 26 inArchive.extract(in, false, new ExtractCallback(inArchive, outputDir.getAbsolutePath(), extractedFiles)); 27 } finally { 28 if (randomAccessFile != null) { 29 randomAccessFile.close(); 30 } 31 if (inArchive != null) { 32 try { 33 inArchive.close(); 34 } catch (SevenZipException e) { 35 e.printStackTrace(); 36 } 37 } 38 } 39 List<File> list=new ArrayList<>(extractedFiles); 40 return list; 41 } 42 43 private static class ExtractCallback implements IArchiveExtractCallback { 44 private IInArchive inArchive; 45 private String outDir; 46 private Set<File> extractedFiles; 47 // 用於跟蹤是否需要關閉流的變數 48 private OutputStream fos = null; 49 private boolean closeStreamAfterOperation = false; // 標記流是否需要關閉 50 51 public ExtractCallback(IInArchive inArchive, String outDir, Set<File> extractedFiles) { 52 this.inArchive = inArchive; 53 this.outDir = outDir; 54 this.extractedFiles = extractedFiles; 55 } 56 57 @Override 58 public void setCompleted(long arg0) throws SevenZipException { 59 } 60 61 @Override 62 public void setTotal(long arg0) throws SevenZipException { 63 } 64 65 66 @Override 67 public ISequentialOutStream getStream(int index, ExtractAskMode extractAskMode) throws SevenZipException { 68 final String path = (String) inArchive.getProperty(index, PropID.PATH); 69 // 這裡不再建立 File 物件,避免多次呼叫 getStream 時重複建立 70 return new ISequentialOutStream() { 71 public int write(byte[] data) throws SevenZipException { 72 File file = new File(outDir, path); 73 try { 74 if (data.length == 0) return 0; 75 file.getParentFile().mkdirs(); // 確保目錄存在 76 if (fos == null) { // 如果這是第一次寫入,初始化輸出流 77 fos = new FileOutputStream(file); 78 closeStreamAfterOperation = true; // 設定標記,表示需要在操作結果後關閉流 79 } 80 fos.write(data); 81 fos.flush(); // 重新整理以確保資料被寫入磁碟 82 extractedFiles.add(file); // 新增到提取檔案集合 83 } catch (IOException e) { 84 throw new SevenZipException("Error writing data to file: " + path, e); 85 } 86 return data.length; 87 } 88 }; 89 } 90 91 @Override 92 public void prepareOperation(ExtractAskMode arg0) throws SevenZipException { 93 } 94 95 @Override 96 public void setOperationResult(ExtractOperationResult extractOperationResult) throws SevenZipException { 97 if (closeStreamAfterOperation && fos != null) { 98 try { 99 // 關閉輸出流 100 fos.close(); 101 } catch (IOException e) { 102 throw new SevenZipException("關閉檔案輸出流時報錯", e); 103 } finally { 104 // 重置標記 105 closeStreamAfterOperation = false; 106 // 清除引用,以便垃圾回收 107 fos = null; 108 } 109 } 110 } 111 112 } 113 }
3、編寫測試類:建立測試類以驗證RAR5檔案的解壓功能。
1 package rar5; 2 3 import java.io.File; 4 import java.io.IOException; 5 import java.util.List; 6 7 public class RAR5ExtractorTest { 8 9 public static void main(String[] args) { 10 File rarDirFile = new File("src/main/resources/rar5Test06.rarbak"); 11 File outDirFile = new File("src/main/resources/temp/rar5Test06.rar"); 12 13 Rar5DocExtractor extractor = new Rar5DocExtractor(); 14 try { 15 List<File> extractedFiles = extractor.extractFiles(rarDirFile, outDirFile); 16 System.out.println("Extracted files:"); 17 for (File file : extractedFiles) { 18 System.out.println(file.getAbsolutePath()); 19 } 20 } catch (IOException e) { 21 e.printStackTrace(); 22 } 23 } 24 }