java 解压 ZIP 中文 乱码 GBK UTF-8 完美解决方案

java解压ZIP 解决中文乱码 (GBK和UTF-8)

本文简书地址

工具使用 : zip4j

GitHub : zip4j

版本 : 2.2.8

Maven :


<dependency>
    <groupId>net.lingala.zip4jgroupId>
    <artifactId>zip4jartifactId>
    <version>2.2.8version>
dependency>

ZIP解压现状

ZIP格式在不同平台上使用不同软件压缩,大致结果为两类:
1. Windows下使用WinRAR、好压、快压、百度压缩等工具压缩的文件
    特点:文件名为GBK编码
2. 使用Linux、MacOS等系统压缩的zip文件
    特点:文件名为UTF-8编码

过往解决方案

通过指定解压时的文件名编码类型来解决,最简单粗暴的就是

ZipFile zip = new ZipFile(dest);
//直接指定GBK,反正大多数人用win操作
zip.setFileNameCharset("GBK");
zip.extractAll(Constants.GOODS_ITEM_IMG_PATH);

但是现在用macOS办公的人越来越多,这种写法已经不满足需求了

新解决方案

通过阅读ZIP的协议文档,我们可以发现,Info-ZIP Unicode Path Extra Field (0x7075)
这个额外信息可以解决我们的问题,据笔者测试,WinRAR和百度压缩等使用GBK作为文件编码的压缩软件,
在这个区域会记录文件名的UTF-8编码的名称,但是因为这个字段不是必要字段,文件名使用UTF-8编码的
MacOS归档、Deepin归档等软件不会填充这个信息。

解决方案代码

     String extractAll(MultipartFile file) throws Exception { 
            String path = RECEIVABLE_SHEET_PATH;
             if(!new File(path).mkdirs()) {
                 return "上传文件失败,无法创建临时文件夹";
             }
             File dest = new File(path + "/"+file.getOriginalFilename());
             file.transferTo(dest);
    
             /* 解压 */
             try {
                ZipFile zip = new ZipFile(dest);
                
                zip.setCharset(Charset.forName("utf-8"));
                 System.out.println("begin unpack zip file....");
                 
                zip.getFileHeaders().forEach(v->{
                    String extractedFile = getFileNameFromExtraData(v);
                    try {
                        zip.extractFile(v, path,extractedFile);
                    } catch (ZipException e) {
                        System.out.println("解压失败 :"+extractedFile);
                        e.printStackTrace();
                        return;
                    }
                    System.out.println("解压成功 :"+extractedFile);
                });
                 System.out.println("unpack zip file success");
            } catch (ZipException e) {
                if(!new File(path).mkdirs())
                    return "解压失败";
            }
             return "success";
        }
     
      public static String getFileNameFromExtraData(FileHeader fileHeader) {
              if(fileHeader.getExtraDataRecords()!=null){
                  for (ExtraDataRecord extraDataRecord : fileHeader.getExtraDataRecords()) {
                      long identifier = extraDataRecord.getHeader();
                      if (identifier == 0x7075) {
                          byte[] bytes = extraDataRecord.getData();
                          ByteBuffer buffer = ByteBuffer.wrap(bytes);
                          byte version = buffer.get();
                          assert (version == 1);
                          int crc32 = buffer.getInt();
                          System.out.println("使用:fileHeader.getExtraDataRecords() ");
                          return new String(bytes, 5, buffer.remaining(), StandardCharsets.UTF_8);
                      }
                  }
              }
              System.out.println("使用:fileHeader.getFileName()");
              return fileHeader.getFileName();
          }

参考ZIP规范资料

Third party mappings commonly used are:

       0x07c8        Macintosh
       0x2605        ZipIt Macintosh
       0x2705        ZipIt Macintosh 1.3.5+
       0x2805        ZipIt Macintosh 1.3.5+
       0x334d        Info-ZIP Macintosh
       0x4341        Acorn/SparkFS
       0x4453        Windows NT security descriptor (binary ACL)
       0x4704        VM/CMS
       0x470f        MVS
       0x4b46        FWKCS MD5 (see below)
       0x4c41        OS/2 access control list (text ACL)
       0x4d49        Info-ZIP OpenVMS
       0x4f4c        Xceed original location extra field
       0x5356        AOS/VS (ACL)
       0x5455        extended timestamp
       0x554e        Xceed unicode extra field
       0x5855        Info-ZIP UNIX (original, also OS/2, NT, etc)
       0x6375        Info-ZIP Unicode Comment Extra Field
       0x6542        BeOS/BeBox
       0x7075        Info-ZIP Unicode Path Extra Field
       0x756e        ASi UNIX
       0x7855        Info-ZIP UNIX (new)
       0xa220        Microsoft Open Packaging Growth Hint
       0xfd4a        SMS/QDOS

-Info-ZIP Unicode Path Extra Field (0x7075):

       Stores the UTF-8 version of the file name field as stored in the
       local header and central directory header. (Last Revision 20070912)

       Value         Size        Description
       -----         ----        -----------
       (UPath) 0x7075        Short       tag for this extra block type ("up")
       TSize         Short       total data size for this block
       Version       1 byte      version of this extra field, currently 1
       NameCRC32     4 bytes     File Name Field CRC32 Checksum
       UnicodeName   Variable    UTF-8 version of the entry File Name

       Currently Version is set to the number 1.  If there is a need
       to change this field, the version will be incremented.  Changes
       may not be backward compatible so this extra field should not be
       used if the version is not recognized.

       The NameCRC32 is the standard zip CRC32 checksum of the File Name
       field in the header.  This is used to verify that the header
       File Name field has not changed since the Unicode Path extra field
       was created.  This can happen if a utility renames the File Name but
       does not update the UTF-8 path extra field.  If the CRC check fails,
       this UTF-8 Path Extra Field should be ignored and the File Name field
       in the header should be used instead.

       The UnicodeName is the UTF-8 version of the contents of the File Name
       field in the header.  As UnicodeName is defined to be UTF-8, no UTF-8
       byte order mark (BOM) is used.  The length of this field is determined
       by subtracting the size of the previous fields from TSize.  If both
       the File Name and Comment fields are UTF-8, the new General Purpose
       Bit Flag, bit 11 (Language encoding flag (EFS)), can be used to
       indicate that both the header File Name and Comment fields are UTF-8
       and, in this case, the Unicode Path and Unicode Comment extra fields
       are not needed and should not be created.  Note that, for backward
       compatibility, bit 11 should only be used if the native character set
       of the paths and comments being zipped up are already in UTF-8. It is
       expected that the same file name storage method, either general
       purpose bit 11 or extra fields, be used in both the Local and Central
       Directory Header for a file.

参考文献

unzip not correct with cjk filename. #45

Garbled chinese character #73

你可能感兴趣的:(javaWeb学习笔记)