提问人:pkaeding 提问时间:9/16/2008 最后编辑:jocepkaeding 更新时间:10/26/2017 访问量:53510
从 ZipInputStream 读取到 ByteArrayOutputStream 中
Reading from a ZipInputStream into a ByteArrayOutputStream
问:
我正在尝试从 中读取单个文件并将其复制到 (这样我就可以创建一个并将其交给最终将关闭流的第 3 方库,并且我不希望我的被关闭)。java.util.zip.ZipInputStream
java.io.ByteArrayOutputStream
java.io.ByteArrayInputStream
ZipInputStream
我可能在这里遗漏了一些基本的东西,但我从不在这里进入 while 循环:
ByteArrayOutputStream streamBuilder = new ByteArrayOutputStream();
int bytesRead;
byte[] tempBuffer = new byte[8192*2];
try {
while ((bytesRead = zipStream.read(tempBuffer)) != -1) {
streamBuilder.write(tempBuffer, 0, bytesRead);
}
} catch (IOException e) {
// ...
}
我缺少什么可以复制流?
编辑:
我应该之前提到这不是来自文件,所以我认为我不能使用 .它来自通过 servlet 上传的文件。ZipInputStream
ZipFile
另外,在进入这段代码之前,我已经调用了。如果我不尝试将文件复制到另一个文件(通过上面提到的),而只是将其传递给我的第三方库,则该库将关闭流,并且我无法执行任何其他操作,例如处理流中的剩余文件。getNextEntry()
ZipInputStream
InputStream
OutputStream
ZipInputStream
答:
检查输入流是否位于乞求中。
否则,作为实现:我认为您不需要在阅读时写入结果流,除非您在另一个线程中处理此确切的流。
只需创建一个字节数组,读取输入流,然后创建输出流。
目前尚不清楚您是如何获得 zipStream 的。当你得到它时,它应该可以工作:
zipStream = zipFile.getInputStream(zipEntry)
评论
我会使用 commons io 项目中的 IOUtils。
IOUtils.copy(zipStream, byteArrayOutputStream);
评论
您可能尝试过从这样的阅读中阅读:FileInputStream
ZipInputStream in = new ZipInputStream(new FileInputStream(...));
这是行不通的,因为 zip 存档可以包含多个文件,并且您需要指定要读取的文件。
您可以使用java.util.zip.ZipFile和库(例如Apache Commons IO的IOUtils或Guava的ByteStreams)来帮助您复制流。
例:
ByteArrayOutputStream out = new ByteArrayOutputStream();
try (ZipFile zipFile = new ZipFile("foo.zip")) {
ZipEntry zipEntry = zipFile.getEntry("fileInTheZip.txt");
try (InputStream in = zipFile.getInputStream(zipEntry)) {
IOUtils.copy(in, out);
}
}
目前尚不清楚您是如何获得 zipStream 的。当你得到它时,它应该可以工作:
zipStream = zipFile.getInputStream(zipEntry)
如果要从 ZipFile 获取 ZipInputStream,则可以获取 3d 参与方库的一个流,让它使用它,然后使用前面的代码获取另一个输入流。
请记住,输入流是一个游标。如果你有整个数据(如 ZipFile),你可以要求 N 个游标覆盖它。
另一种情况是,如果您只有一个“GZip”输入流,只有一个压缩字节流。在这种情况下,ByteArrayOutputStream 缓冲区就很有意义了。
我会在 ZipInputStream 上调用 getNextEntry(),直到它位于您想要的条目处(使用 ZipEntry.getName() 等)。调用 getNextEntry() 会将“光标”前进到它返回的条目的开头。然后,使用 ZipEntry.getSize() 确定应该使用 zipInputStream.read() 读取多少字节。
评论
您可以围绕忽略 close() 的 ZipInputStream 实现自己的包装器,并将其移交给第三方库。
thirdPartyLib.handleZipData(new CloseIgnoringInputStream(zipStream));
class CloseIgnoringInputStream extends InputStream
{
private ZipInputStream stream;
public CloseIgnoringInputStream(ZipInputStream inStream)
{
stream = inStream;
}
public int read() throws IOException {
return stream.read();
}
public void close()
{
//ignore
}
public void reallyClose() throws IOException
{
stream.close();
}
}
您的循环看起来有效 - 以下代码(仅靠它自己)返回什么?
zipStream.read(tempBuffer)
如果它返回 -1,则 zipStream 在你得到它之前就关闭了,并且所有赌注都关闭了。是时候使用调试器并确保传递给您的内容实际上有效了。
当您调用 getNextEntry() 时,它是否返回一个值,以及条目中的数据是否有意义(即 getCompressedSize() 是否返回有效值)?如果您只是在读取未嵌入预读 zip 条目的 Zip 文件,则 ZipInputStream 不适合您。
关于 Zip 格式的一些有用花絮:
嵌入在 zip 文件中的每个文件都有一个标题。此标头可以包含有用的信息(例如流的压缩长度、文件中的偏移量、CRC) - 或者它可以包含一些神奇的值,这些值基本上说“信息不在流标头中,您必须检查 Zip post-amble”。
然后,每个 zip 文件都有一个表,该表附加到文件末尾,其中包含所有 zip 条目以及实际数据。末尾的表是必填的,其中的值必须正确。相反,不必提供流中嵌入的值。
如果使用 ZipFile,它将读取 zip 末尾的表。如果您使用 ZipInputStream,我怀疑 getNextEntry() 会尝试使用流中嵌入的条目。如果未指定这些值,则 ZipInputStream 不知道流可能有多长。inflate 算法是自终止的(您实际上不需要知道输出流的未压缩长度即可完全恢复输出),但此读取器的 Java 版本可能无法很好地处理这种情况。
我要说的是,让 servlet 返回 ZipInputStream 是相当不寻常的(如果您要接收压缩内容,接收 inflatorInputStream 更为常见。
评论
请尝试以下代码
private static byte[] getZipArchiveContent(File zipName) throws WorkflowServiceBusinessException {
BufferedInputStream buffer = null;
FileInputStream fileStream = null;
ByteArrayOutputStream byteOut = null;
byte data[] = new byte[BUFFER];
try {
try {
fileStream = new FileInputStream(zipName);
buffer = new BufferedInputStream(fileStream);
byteOut = new ByteArrayOutputStream();
int count;
while((count = buffer.read(data, 0, BUFFER)) != -1) {
byteOut.write(data, 0, count);
}
} catch(Exception e) {
throw new WorkflowServiceBusinessException(e.getMessage(), e);
} finally {
if(null != fileStream) {
fileStream.close();
}
if(null != buffer) {
buffer.close();
}
if(null != byteOut) {
byteOut.close();
}
}
} catch(Exception e) {
throw new WorkflowServiceBusinessException(e.getMessage(), e);
}
return byteOut.toByteArray();
}
您错过了来电
ZipEntry 条目 = (ZipEntry) zipStream.getNextEntry();
定位第一个条目解压缩的第一个字节。
ByteArrayOutputStream streamBuilder = new ByteArrayOutputStream();
int bytesRead;
byte[] tempBuffer = new byte[8192*2];
ZipEntry entry = (ZipEntry) zipStream.getNextEntry();
try {
while ( (bytesRead = zipStream.read(tempBuffer)) != -1 ){
streamBuilder.write(tempBuffer, 0, bytesRead);
}
} catch (IOException e) {
...
}
评论