【问题标题】:Java NIO Zip Filesystem equivalent of setMethod() in java.util.zip.ZipEntryJava NIO Zip 文件系统等效于 java.util.zip.ZipEntry 中的 setMethod()
【发布时间】:2015-01-30 15:50:15
【问题描述】:

我有一些现有的代码可以创建 Epub 2 格式的 zip 文件,它可以正常工作。

在尝试更新我的代码以支持 Epub 3 格式时,我想我会尝试使用 Java NIO Zip 文件系统而不是 java.util.zip.ZipFile。除了一件小物件外,我几乎到了。

Epub 格式需要一个 20 字节的 mimetype 文件,必须以未压缩的形式放入 zip 中。 java.util.zip.ZipEntry api 提供了setMethod(ZipEntry.STORED) 来实现这一点。

我在 Java NIO FileSystem API 文档中找不到对此的任何引用。有没有 ZipEntry.setMethod() 的等价物?

编辑 1

好的,我知道了如何显示属性,感谢您提供的示例,但我找不到任何关于如何创建属性的文档,例如 (zip:method,0 ),甚至在 Oracle 自己的 oracle 上。在我看来,Java 7 中的 NIO 增强功能只记录了大约 20%。 attributes api doc 非常稀疏,尤其是如何创建属性。

我开始得到的感觉是,NIO Zip 文件系统可能不是对java.util.zip 的改进,并且需要更多代码才能达到相同的结果。

编辑 2

我尝试了以下方法:

String contents = "application/epub+zip"; /* contents of mimetype file */
Map<String, String> map = new HashMap<>();
map.put("create", "true");

Path zipPath = Paths.get("zipfstest.zip");
Files.deleteIfExists(zipPath);

URI fileUri = zipPath.toUri(); // here
URI zipUri = new URI("jar:" + fileUri.getScheme(), fileUri.getPath(), null);

try (FileSystem zipfs = FileSystems.newFileSystem(zipUri, map)) {
    Path pathInZip = zipfs.getPath("mimetype");
    Files.createFile(pathInZip, new ZipFileAttribute<Integer>("zip:method", 0));
    byte[] bytes = contents.getBytes();
    Files.write(pathInZip, bytes, StandardOpenOption.WRITE);
    }

ZipFileAttribute 类是属性接口的最小实现。我可以发布它,但它只是一个键值对(名称,值)

此代码成功创建了 zipFile,但是当我使用 7zip 打开 zipFile 时,我看到 mimetype 文件以 DEFLATED (8) 的形式存储在 zip 中,而不是我需要的 STORED (0)。所以问题是,我如何正确编码属性以便它存储为 STORED。

【问题讨论】:

    标签: java zip compression nio


    【解决方案1】:

    这不是很好的文档,但 JDK 的 zip 文件系统提供程序支持名为 zipFileAttributeView

    这是我的 zip 中的代码:

    public static void main(final String... args)
        throws IOException
    {
        final Path zip = Paths.get("/home/fge/t.zip");
        final Map<String, ?> env = Collections.singletonMap("readonly", "true");
        final URI uri = URI.create("jar:" + zip.toUri());
    
        try (
            final FileSystem fs = FileSystems.newFileSystem(uri, env);
        ) {
            final Path slash = fs.getPath("/");
            Files.readAttributes(slash, "zip:*").forEach( (key, val) -> {
                System.out.println("Attribute name: " + key);
                System.out.printf("Value: %s (class: %s)\n", val,
                    val != null ? val.getClass(): "N/A");
            });
        }
    }
    

    输出:

    Attribute name: size
    Value: 0 (class: class java.lang.Long)
    Attribute name: creationTime
    Value: null (class: N/A)
    Attribute name: lastAccessTime
    Value: null (class: N/A)
    Attribute name: lastModifiedTime
    Value: 1969-12-31T23:59:59.999Z (class: class java.nio.file.attribute.FileTime)
    Attribute name: isDirectory
    Value: true (class: class java.lang.Boolean)
    Attribute name: isRegularFile
    Value: false (class: class java.lang.Boolean)
    Attribute name: isSymbolicLink
    Value: false (class: class java.lang.Boolean)
    Attribute name: isOther
    Value: false (class: class java.lang.Boolean)
    Attribute name: fileKey
    Value: null (class: N/A)
    Attribute name: compressedSize
    Value: 0 (class: class java.lang.Long)
    Attribute name: crc
    Value: 0 (class: class java.lang.Long)
    Attribute name: method
    Value: 0 (class: class java.lang.Integer)
    

    看起来像“zip:method”属性就是你想要的。

    所以,如果你想改变方法,如果你有一个Path 到你的 zip 文件系统,看起来你可以做(​​未经测试!):

    Files.setAttribute(thePath, "zip:method", ZipEntry.DEFLATED);
    

    【讨论】:

    • ZipFileAttributeView#setAttribute 仅更新OpenJDK 8lastModifiedTimelastAccessTimecreationTime 的属性值。我的测试显示设置属性zip:method 没有按预期工作。
    【解决方案2】:

    根据下面非常有趣的回复,我重新阅读了所有可用的 API 文档,并尝试了各种猜测来设置属性。我在两者上都画了一个空白。我不得不得出这样的结论:这个功能充其量是未记录的,甚至可能在 Java NIO 中不可用。因此,任何想以 EPUB 格式创建文件的人都必须像我一样继续使用旧的基于 ZipFile 的 API。另一种方法是使用用ZipFile 或其他语言创建的shell zip,然后切换到NIO。这两个选项——怎么说呢——有点不雅。我认为它的目的是让 java.io.File 随着时间的推移而被半弃用。

    【讨论】:

    • 你说得对,我查看了 ZipFileSystem.java 的 openJDK 实现,可悲的是,在编写 zip 文件时,该方法被硬编码为“DEFLATED”并且不能被覆盖与“存储”:u.method = METHOD_DEFLATED;
    【解决方案3】:

    在 Java8 中无法设置压缩方法(在 OpenJDK 中,其他供应商可能有不同的实现),因为它是硬编码的。在 Java11 中 jdk.zipfs 模块没有文档,但是通过查看源代码,jdk.nio.zipfs.ZipFileSystem 中出现了一个新选项 noCompression。自 Java14 以来,这些选项终于被完整记录:https://docs.oracle.com/en/java/javase/14/docs/api/jdk.zipfs/module-summary.html

    FileSystems.newFileSystem(URI uri, Map&lt;String,?&gt; env) 可用于向文件系统添加其他选项。

    例如(Java11):

    try (FileSystem zipFs = FileSystems.newFileSystem(
        new URI("jar:file:///tmp/test.zip"),
        Map.of("noCompression", true, "create", true))) {
      Path testFile = zipFs.getPath("/test.txt");
      try (OutputStream os = Files.newOutputStream(testFile)) {
        os.write("Testing uncompressed text".getBytes(StandardCharsets.UTF_8));
      }
    }
    

    将创建一个 zip 文件 /tmp/test.zip,其中包含一个 25 字节长且未压缩的条目 /test.txt

    Java14 添加了一个新选项compressionMethod,其值为STOREDDEFLATED,但支持noCompression 选项以实现向后兼容性。

    【讨论】:

      猜你喜欢
      • 2023-03-19
      • 1970-01-01
      • 1970-01-01
      • 2017-09-19
      • 1970-01-01
      • 2016-12-18
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多