先上正确方法:
正确方式应该为,先创建一个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文档中是这么写的
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混用总觉得怪怪的。。。