【问题标题】:Creating zip with directory containing special characters使用包含特殊字符的目录创建 zip
【发布时间】:2017-01-09 21:50:37
【问题描述】:

我正在尝试创建一个包含一些目录的 zip 存档。一些目录的名称中有波兰语字母,例如:ą、ę、ł 等。一切看起来都很好,只是对于名称中有特殊字母的任何目录,在 zip 文件中创建了另一个目录。以下代码有什么问题:

import java.io.File;
import java.io.IOException;
import java.net.URI;
import java.net.URISyntaxException; 
import java.nio.file.*;
import java.nio.file.attribute.BasicFileAttributes;
import java.util.Collections;
import java.util.HashMap;
import java.util.Map;

public class Main {

  public static void main(String[] args) throws URISyntaxException, IOException {
    Map<String, String> env = new HashMap<>();
    env.put("create", "true");
    URI fileUri = new File("zipfs.zip").toPath().toUri();
    URI zipUri = new URI("jar:" + fileUri.getScheme(), fileUri.getPath(), null);

    try (FileSystem zipfs = FileSystems.newFileSystem(zipUri, env)) {

        Path directory = zipfs.getPath("ą");
        Files.createDirectory(directory);
        Path pathInZipfile = directory.resolve("someFile.txt");
        Path source = Paths.get("source.txt");

        Files.copy(source, pathInZipfile, StandardCopyOption.REPLACE_EXISTING);
    }

    FileSystem zipFs = FileSystems.newFileSystem(zipUri, Collections.emptyMap());

    Path root = zipFs.getPath("/");

    Files.walkFileTree(root, new SimpleFileVisitor<Path>() {
        @Override
        public FileVisitResult visitFile(Path path, BasicFileAttributes attrs) throws IOException {
            System.out.println(path);
            return FileVisitResult.CONTINUE;
        }

        @Override
        public FileVisitResult preVisitDirectory(Path dir, BasicFileAttributes attrs) throws IOException {
            System.out.println(dir);
            return super.preVisitDirectory(dir, attrs);
        }
    });
  }
}

这个程序的输出和预期的一样:

/
/ą/
/ą/someFile.txt

但是当你打开创建的 zip 文件时,里面有两个目录:

Ä?  
ą

第一个是空的,文本文件应该在“±”目录中。

【问题讨论】:

    标签: java zip


    【解决方案1】:

    ZipFileSystem 似乎没有为文件夹设置语言编码标志 (EFS)。该标志基本上表示“此路径使用 UTF-8”。

    让我们看看zipdetails(跳过不感兴趣的行):

    0072 CENTRAL HEADER #1     02014B50
    007A General Purpose Flag  0000                       // <= no EFS flag
    00A0 Filename              'ą/'
    
    00AC CENTRAL HEADER #2     02014B50
    00B4 General Purpose Flag  0800
         [Bits 1-2]            0 'Normal Compression'
         [Bit 11]              1 'Language Encoding'      // <= EFS flag
    00DA Filename              'ą/someFile.txt'
    

    否则,ą/ 以 UTF-8 正确编码。

    如果没有此标志,则由读取/提取 zip 文件的程序来选择编码(通常是系统默认值)。 unzip 在这里不好用:

    $ unzip -t zipfs.zip 
    Archive:  zipfs.zip
        testing: -à/                      OK
        testing: ą/someFile.txt          OK
    No errors detected in compressed data of zipfs.zip.
    

    请注意,如果您使用 -UU 禁用 unicode 支持,则会在两个条目中得到

    7z 在这里效果更好(但这只是因为我的系统默认编码是 UTF-8):

    $ 7z l zipfs.zip
    ...
       Date      Time    Attr         Size   Compressed  Name
    ------------------- ----- ------------ ------------  ------------------------
    2017-01-10 22:51:14 D....            0            0  ą
    2017-01-10 22:51:15 .....            0            2  ą/someFile.txt
    ------------------- ----- ------------ ------------  ------------------------
    2017-01-10 22:51:15                  0            2  1 files, 1 folders
    

    如果您不能强制打开 zip 文件的方式(例如,如果 zip 文件发送给用户而不是您的服务器之一)或仅在文件夹中使用 ASCII 字符,则使用不同的库看起来像唯一的解决方案。

    【讨论】:

    • 不错的答案。在 macOS High Sierra 上,如果我只是双击 zip 文件,它会解压缩到“±”目录。但是,如果我运行unzip zipfs.zip,我会得到Archive: zipfs.zip creating: -�/ error: cannot create -�/someFile.txt No such file or directory,并且只会创建一个空目录-%E0。所以这确实很大程度上取决于解包程序。
    • 这是一个很好的答案,为我解决了一个很大的谜团。但是有解决方法吗?有没有办法告诉ZipFileSystem 为文件夹设置EFS 标志?