解压乱码,判断 zip 压缩包编码是 GBK,还是 UTF-8

时间 2020/8/18 11:36:13 加载中...

问题

java 程序解压 zip 类型的压缩包,使用 WinRAR 压缩的解压出来就会乱码。
我使用的是 zip4j 解压的,根本原因还是解压时候编码问题。

如何鉴别文件编码

网络上提供的几种都以失败告终:

1、判断文件首字节
2、使用 cpdetector

鉴别编码失败,怎么办?

有个有趣的现象:如果自己写解压程序的话,编码不对会报异常,而不是乱码了。
因此我们可以使用异常来判断是否可行。

自己写解压

  1. public static void doUnArchiver(File srcfile, String destpath) throws IOException {
  2. byte[] buf = new byte[1024];
  3. FileInputStream fis = new FileInputStream(srcfile);
  4. BufferedInputStream bis = new BufferedInputStream(fis);
  5. ZipInputStream zis = new ZipInputStream(bis);
  6. ZipEntry zn = null;
  7. while ((zn = zis.getNextEntry()) != null) {
  8. File f = new File(destpath + "/" + zn.getName());
  9. if (zn.isDirectory()) {
  10. f.mkdirs();
  11. } else {
  12. /*
  13. * 父目录不存在则创建
  14. */
  15. File parent = f.getParentFile();
  16. if (!parent.exists()) {
  17. parent.mkdirs();
  18. }
  19. FileOutputStream fos = new FileOutputStream(f);
  20. BufferedOutputStream bos = new BufferedOutputStream(fos);
  21. int len;
  22. while ((len = zis.read(buf)) != -1) {
  23. bos.write(buf, 0, len);
  24. }
  25. bos.flush();
  26. bos.close();
  27. }
  28. zis.closeEntry();
  29. }
  30. zis.close();
  31. }

对其改造,进行判断

  1. private boolean testEncoding(String filepath, Charset charset) throws FileNotFoundException {
  2. FileInputStream fis = new FileInputStream(new File(filepath));
  3. BufferedInputStream bis = new BufferedInputStream(fis);
  4. ZipInputStream zis = new ZipInputStream(bis, charset);
  5. ZipEntry zn = null;
  6. try {
  7. while ((zn = zis.getNextEntry()) != null) {
  8. // do nothing
  9. }
  10. } catch (Exception e) {
  11. return false;
  12. }finally {
  13. try {
  14. zis.close();
  15. bis.close();
  16. fis.close();
  17. } catch (IOException e) {
  18. e.printStackTrace();
  19. }
  20. }
  21. return true;
  22. }

测试结果如下:

  1. System.out.println(testEncoding("d:/UTF8的文件.zip", Charset.forName("UTF-8"))); // true
  2. System.out.println(testEncoding("d:/UTF8的文件.zip", Charset.forName("GBK"))); //true
  3. System.out.println(testEncoding("d:/GBK的文件.zip", Charset.forName("UTF-8"))); //false
  4. System.out.println(testEncoding("d:/GBK的文件.zip", Charset.forName("GBK"))); //true

注意:
对于 UTF-8 的文件,使用 GBK 返回的居然是 true
所以我们在使用 testEncoding 方法的时候,只能是测试 “UTF-8” 返回的是不是 false
如果是 false,则使用 GBK 编码

  1. if (testEncoding(file, Charset.forName("UTF-8")) == false){
  2. zipFile.setCharset(Charset.forName("GBK"));
  3. }

整体代码

pom.xml 引入 zip4j

  1. <dependency>
  2. <groupId>net.lingala.zip4j</groupId>
  3. <artifactId>zip4j</artifactId>
  4. <version>2.6.1</version>
  5. </dependency>

解压代码

  1. // ZipFile 默认是使用 UTF-8 编码处理文件
  2. ZipFile zipFile = new ZipFile(file);
  3. gl>if (testEncoding(file, Charset.forName("UTF-8")) == false){
  4. gl>zipFile.setCharset(Charset.forName("GBK"));
  5. gl>}
  6. zipFile.extractAll(baseDir);
  7. private boolean testEncoding(String filepath, Charset charset) throws FileNotFoundException {
  8. FileInputStream fis = new FileInputStream(new File(filepath));
  9. BufferedInputStream bis = new BufferedInputStream(fis);
  10. ZipInputStream zis = new ZipInputStream(bis, charset);
  11. ZipEntry zn = null;
  12. try {
  13. while ((zn = zis.getNextEntry()) != null) {
  14. // do nothing
  15. }
  16. } catch (Exception e) {
  17. return false;
  18. }finally {
  19. try {
  20. zis.close();
  21. bis.close();
  22. fis.close();
  23. } catch (IOException e) {
  24. e.printStackTrace();
  25. }
  26. }
  27. return true;
  28. }

更新

在使用上面的解压程序时,又出现了新的问题。

有一压缩包 shuju.zip ,里面的内容为:

├── shuju
———└── 新建文件夹
——————-└── 李贽与晚明文学思想.pdf
———└── China-Africa Relations, Political Conditions,and Ngũgĩ Wa Thiong’o’s Wizard of the Crow Peter Leman.PDF
———└── 一手数据.xlsx

shuju.zip 文件下载地址:链接:https://pan.baidu.com/s/12ugoP2Vx8Ui7chKVGaq-1w 提取码:w6qq

解压之后,中文文件没有乱码,但其中一个英文文件乱码了。如下图:


备用:http://www.sqber.com:8007/weibo/pic?url=https://img-blog.csdnimg.cn/20210419103212615.png

解决办法:

改用 apache 下的一款解压缩工具 : Apache Commons Compress

https://commons.apache.org/proper/commons-compress/examples.html
https://github.com/apache/commons-compress

具体使用如下:

pom.xml 文件引入

  1. <!-- 解压缩 https://mvnrepository.com/artifact/org.apache.commons/commons-compress -->
  2. <dependency>
  3. <groupId>org.apache.commons</groupId>
  4. <artifactId>commons-compress</artifactId>
  5. <version>1.18</version>
  6. </dependency>

解压代码:

  1. public static void decompressor(String zipFile, String targetDir) throws IOException, ArchiveException {
  2. File archiveFile = new File(zipFile);
  3. // 文件不存在,跳过
  4. if (!archiveFile.exists())
  5. return;
  6. ArchiveInputStream input = new ArchiveStreamFactory().createArchiveInputStream(new BufferedInputStream(new FileInputStream(zipFile)));
  7. ArchiveEntry entry = null;
  8. while ((entry = input.getNextEntry()) != null) {
  9. if (!input.canReadEntryData(entry)) {
  10. // log something?
  11. continue;
  12. }
  13. String name = Paths.get(targetDir, entry.getName()).toString();
  14. File f = new File(name);
  15. if (entry.isDirectory()) {
  16. if (!f.isDirectory() && !f.mkdirs()) {
  17. throw new IOException("failed to create directory " + f);
  18. }
  19. } else {
  20. File parent = f.getParentFile();
  21. if (!parent.isDirectory() && !parent.mkdirs()) {
  22. throw new IOException("failed to create directory " + parent);
  23. }
  24. try (OutputStream o = Files.newOutputStream(f.toPath())) {
  25. IOUtils.copy(input, o);
  26. }
  27. }
  28. }
  29. input.close();
  30. }
扫码分享
版权说明
作者:SQBER
文章来源:http://www.sqber.com/articles/zip-file-gbk-utf-8.html
本文版权归作者所有,欢迎转载,但未经作者同意必须保留此段声明,且在文章页面明显位置给出原文连接,否则保留追究法律责任的权利。