如何在D盘根目录创建TrueZip归档文件?
我之前也踩过TrueZip这个坑!它对系统盘的根目录(比如D:\这种顶级目录)有默认的保护机制,哪怕你的D盘只是用来存个人数据,它也会把这里识别为「系统文件系统根」,限制直接创建新的归档文件——这就是你看到FsArchiveFileSystemException: <file system root> (only files can get replaced)错误的核心原因:TrueZip只允许替换根目录下已存在的文件,不允许新建。
下面给你几个可行的解决方案,结合你的代码来调整:
方案1:提前创建空归档文件,再用TrueZip替换它
既然TrueZip允许替换根目录已存在的文件,那我们可以先通过代码创建一个空的zip文件占位,再让TrueZip往里面写入内容。修改你的代码如下:
public static void main(String[] args) throws IOException { JFileChooser chooser = new JFileChooser(); chooser.setDialogTitle("Please select the target folder."); chooser.setFileSelectionMode(JFileChooser.FILES_ONLY); JFrame applicationFrame = new JFrame(); if (chooser.showSaveDialog(applicationFrame) != JFileChooser.APPROVE_OPTION) return; File selectedFile = chooser.getSelectedFile(); String zipPath = selectedFile + ".zip"; File tempZip = new File(zipPath); // 检查目标文件是否在磁盘根目录 File parentDir = tempZip.getParentFile(); if (parentDir != null && parentDir.getAbsolutePath().matches("^[A-Za-z]:\\\\$")) { // 先创建空的zip文件占位 try (OutputStream os = new FileOutputStream(tempZip)) { // 仅创建文件,无需写入内容 } catch (IOException e) { JOptionPane.showMessageDialog(applicationFrame, "无法创建空文件:" + e.getMessage()); return; } } TFile archiveFile = new TFile(zipPath); System.out.println("Selected file: " + archiveFile.getAbsolutePath()); ZipOutputStream zipOutputStream = new ZipOutputStream(new TFileOutputStream(archiveFile)); // 新增空判断,避免files为null时抛出异常 File folderToZip = new File("D:/filesToZip"); File[] files = folderToZip.listFiles(); if (files != null) { for (File file : files) { BufferedInputStream bis = new BufferedInputStream(new FileInputStream(file)); int avail = bis.available(); byte[] buffer = new byte[avail]; if (avail > 0) { bis.read(buffer, 0, avail); } bis.close(); ZipEntry zipEntry = new ZipEntry(file.getName()); zipOutputStream.putNextEntry(zipEntry); zipOutputStream.write(buffer, 0, buffer.length); zipOutputStream.closeEntry(); } } zipOutputStream.close(); System.exit(0); }
方案2:将归档文件放在D盘的子目录中
这是更简单稳妥的办法,直接避开根目录的特殊限制。你可以引导用户选择D盘下的子文件夹(比如D:\Archives),或者在代码里自动处理:如果用户选了根目录,就自动创建一个子目录来存放归档文件。
修改代码中处理选中文件的部分:
File selectedFile = chooser.getSelectedFile(); File parentDir = selectedFile.getParentFile(); // 判断是否选中了磁盘根目录 if (parentDir != null && parentDir.getAbsolutePath().matches("^[A-Za-z]:\\\\$")) { // 创建默认的归档存储子目录 File subDir = new File(parentDir, "ArchiveFiles"); if (!subDir.exists()) { subDir.mkdirs(); // 递归创建目录 } // 将目标文件路径转移到子目录下 selectedFile = new File(subDir, selectedFile.getName()); } TFile archiveFile = new TFile(selectedFile + ".zip");
方案3:修改TrueZip配置(不推荐,版本兼容性差)
部分旧版本的TrueZip支持通过配置禁用根目录保护,但这个方法对版本依赖极强,且可能带来安全风险。如果一定要尝试,可以在代码开头添加:
import de.schlichtherle.truezip.fs.FsConfig; // 禁用根目录归档检测限制 FsConfig.get().setArchiveDetector(FsConfig.get().getArchiveDetector().with(null));
注意:该方法在新版本TrueZip中可能已经失效,优先推荐前两个方案。
你提供的原始代码:
import de.schlichtherle.truezip.file.TFile; import de.schlichtherle.truezip.file.TFileOutputStream; import de.schlichtherle.truezip.zip.ZipEntry; import de.schlichtherle.truezip.zip.ZipOutputStream; import javax.swing.*; import java.io.*; public class MainClass { public static void main(String[] args) throws IOException { JFileChooser chooser = new JFileChooser(); chooser.setDialogTitle("Please select the target folder."); chooser.setFileSelectionMode(JFileChooser.FILES_ONLY); JFrame applicationFrame = new JFrame(); if (chooser.showSaveDialog(applicationFrame) != JFileChooser.APPROVE_OPTION) return; File selectedFile = chooser.getSelectedFile(); TFile archiveFile = new TFile(selectedFile + ".zip"); System.out.println("Selected file: " + archiveFile.getAbsolutePath()); ZipOutputStream zipOutputStream = new ZipOutputStream(new TFileOutputStream(archiveFile)); File folderToZip = new File("D:/filesToZip"); File[] files = folderToZip.listFiles(); for (File file : files) { BufferedInputStream bis = new BufferedInputStream(new FileInputStream(file)); int avail = bis.available(); byte[] buffer = new byte[avail]; if (avail > 0) { bis.read(buffer, 0, avail); } bis.close(); ZipEntry zipEntry = new ZipEntry(file.getName()); zipOutputStream.putNextEntry(zipEntry); zipOutputStream.write(buffer, 0, buffer.length); zipOutputStream.closeEntry(); } zipOutputStream.close(); System.exit(0); } }
你遇到的错误信息:
Selected file: D:\test.zip
Exception in thread "main" java.io.FileNotFoundException: D:\test.zip
at de.schlichtherle.truezip.file.TFileOutputStream.newOutputStream(TFileOutputStream.java:147)
at de.schlichtherle.truezip.file.TFileOutputStream.(TFileOutputStream.java:116)
at MainClass.main(MainClass.java:21)
Caused by: de.schlichtherle.truezip.fs.FsArchiveFileSystemException:(only files can get replaced)
at de.schlichtherle.truezip.fs.FsArchiveFileSystem.mknod(FsArchiveFileSystem.java:379)
at de.schlichtherle.truezip.fs.FsBasicArchiveController$1Output.mknod(FsBasicArchiveController.java:273)
at de.schlichtherle.truezip.fs.FsBasicArchiveController$1Output.newOutputStream(FsBasicArchiveController.java:233)
at de.schlichtherle.truezip.fs.FsContextController$Output.newOutputStream(FsContextController.java:322)
at de.schlichtherle.truezip.fs.FsResourceController$Output.newOutputStream(FsResourceController.java:283)
at de.schlichtherle.truezip.socket.DelegatingOutputSocket.newOutputStream(DelegatingOutputSocket.java:57)
at de.schlichtherle.truezip.fs.FsSyncController$Output.newOutputStream(FsSyncController.java:454)
at de.schlichtherle.truezip.fs.FsLockController$Output$1NewOutputStream.call(FsLockController.java:509)
at de.schlichtherle.truezip.fs.FsLockController$Output$1NewOutputStream.call(FsLockController.java:506)
at de.schlichtherle.truezip.fs.FsLockController.locked(FsLockController.java:328)
at de.schlichtherle.truezip.fs.FsLockController.writeLocked(FsLockController.java:268)
at de.schlichtherle.truezip.fs.FsLockController$Output.newOutputStream(FsLockController.java:513)
at de.schlichtherle.truezip.fs.FsFinalizeController$Output.newOutputStream(FsFinalizeController.java:209)
at de.schlichtherle.truezip.fs.FsFalsePositiveArchiveController$1Output$NewOutputStream.call(FsFalsePositiveArchiveController.java:409)
at de.schlichtherle.truezip.fs.FsFalsePositiveArchiveController$1Output$NewOutputStream.call(FsFalsePositiveArchiveController.java:402)
at de.schlichtherle.truezip.fs.FsFalsePositiveArchiveController$TryChild.call(FsFalsePositiveArchiveController.java:507)
at de.schlichtherle.truezip.fs.FsFalsePositiveArchiveController.call(FsFalsePositiveArchiveController.java:104)
at de.schlichtherle.truezip.fs.FsFalsePositiveArchiveController$1Output.newOutputStream(FsFalsePositiveArchiveController.java:399)
at de.schlichtherle.truezip.file.TFileOutputStream.newOutputStream(TFileOutputStream.java:143)
... 2 more
内容的提问来源于stack exchange,提问作者fukiburi

