• 使用commons-compress解压GBK格式winzip文件到UTF8,以及错误使用ZipArchiveInputStream读出来数据全是空的解决办法


      先上正确方法:

      正确方式应该为,先创建一个ZipFile,然后对其entries做遍历,每一个entry其实就是一个文件或者文件夹,检测到文件夹的时候创建文件夹,其他情况创建文件,其中使用zipFile.getInputStream(entry)可以获得当前文件的输入流(注意是文件的输入流不是压缩文件的输入流)。然后把它写到writer里就可以了。嘛,明明很简单的。下面是一个例子,读取GBK格式的压缩包,压缩包中的文件编码也为GBK格式(就是在windows下写的文件并打包的情况),输出为UTF8的解压(跨平台使用)。

      def decompressZip(source: File, dest: String, sourceCharacters: String = "GBK", destCharacters: String = "UTF-8") = {
          if (source.exists) {
              var os: OutputStream = null
              var inputStream: InputStreamReader = null
              var outWriter: OutputStreamWriter = null
                val zipFile = new ZipFile(source, sourceCharacters)
                var entries = zipFile.getEntries
                
                entries.foreach(entry =>
                    if (entry.isDirectory())
                        new File(dest + entry.getName).mkdirs()                    
                    else if (entry != null) {
                      try{
                          val name = entry.getName
                          val path = dest + name
                          var content = new Array[Char](entry.getSize.toInt)                      
                          inputStream = new InputStreamReader(zipFile.getInputStream(entry), sourceCharacters)
                          println(inputStream.read(content))
                  val entryFile = new File(path)
                          checkFileParent(entryFile)
                  os = new FileOutputStream(entryFile)
                          outWriter = new OutputStreamWriter(os, destCharacters);
                          outWriter.write(new String(content))
                      } catch {
                          case e: Throwable => e.printStackTrace()
                      }finally{
                            if (os != null){
                                os.flush
                                os.close
                            }
                            if (outWriter != null){
                                outWriter.flush
                                outWriter.close
                            }
                            if (inputStream != null) inputStream.close
                        }
                  })
              zipFile.close
          }
      }

      错误示范:

      不知道为什么,网上很多教程都是使用ZipArchiveInputStream来进行解压,然而:

      The ZipFile class is preferred when reading from files as ZipArchiveInputStream is limited by not being able to read the central directory header before returning entries. In particular ZipArchiveInputStream

    • may return entries that are not part of the central directory at all and shouldn't be considered part of the archive.
    • may return several entries with the same name.
    • will not return internal or external attributes.
    • may return incomplete extra field data.
    • may return unknown sizes and CRC values for entries until the next entry has been reached if the archive uses the data descriptor feature.

      在commons-compress的1.3版本就开始建议使用ZipFile了。

      我个人而言,尝试过ZipArchiveInputStream之后发现一个问题,ZipArchiveInputStream创建方式很麻烦,需要指定一个InputStream,而这个方法在API文档中是这么写的

    Constructors
    Constructor and Description
    ZipArchiveInputStream(InputStream inputStream)
    Create an instance using UTF-8 encoding
    ZipArchiveInputStream(InputStream inputStream, String encoding)
    Create an instance using the specified encoding
    ZipArchiveInputStream(InputStream inputStream, String encoding, boolean useUnicodeExtraFields)
    Create an instance using the specified encoding
    ZipArchiveInputStream(InputStream inputStream, String encoding, boolean useUnicodeExtraFields, boolean allowStoredEntriesWithDataDescriptor)
    Create an instance using the specified encoding

      Parameters:inputStream - the stream to wrap

      这个构造方法没有指明这个inputStream参数是什么东西,照网上的方法试了试,使用:  

    val zipFile = new ZipFile(source, sourceCharacters)
    var entries = zipFile.getEntries
    entries.foreach(entry =>
        if (entry != null) {
            try{
                val name = entry.getName
                val path = dest + name
                var content = new Array[Char](entry.getSize.toInt)
                zais = new ZipArchiveInputStream(zipFile.getInputStream(entry))
                val entryFile = new File(path)
            checkFileParent(entryFile)
                   os = new FileOutputStream(entryFile)  
                IOUtils.copy(zais, os)
    ………………

      读出来的数据是空,使用zais.read读出Array[Byte]并把它转化为字符串发现是空白符字符串,直接输出Array[Byte]发现都是0。后来看文档大概知道是什么原因,这个ZipArchiveInputStream读取的应该是Zip文件,然而zipFile.geiInputStream返回的是解压完的文件的输入流,所以才会出现这个问题,试了试commons-compress spark依赖12年出的1.4版本和最新的1.14版本这种方法都是错的,所以我怀疑他们12年之后转的那些博客并没有经过自己使用和测试就转发了。这个ZipFile和ZipArchiveInputStream混用总觉得怪怪的。。。

  • 相关阅读:
    Android 如何自定义EditText 下划线?
    一步一步理解 Java 企业级应用的可扩展性
    客户案例—北京优络时代科技有限公司
    11个显著提升 ASP.NET 应用程序性能的技巧——第1部分
    如何用 React Native 创建一个iOS APP?(二)
    如何与 DevOps 为伍?
    通过 DevOps 整合开发和应用安全管道
    性能为王:选择模拟监控的10大理由!
    模拟监控和真实用户体验监测,选哪个?
    PHP 之 Laravel 框架安装及相关开源软件
  • 原文地址:https://www.cnblogs.com/gaoze/p/7610492.html
Copyright © 2020-2023  润新知