Java 如何使用 InputStream 从 ZIP 读取文件?
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/23869228/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
How to read file from ZIP using InputStream?
提问by Tony
I must get file content from ZIP archive (only one file, I know its name) using SFTP. The only thing I'm having is ZIP's InputStream
. Most examples show how get content using this statement:
我必须使用 SFTP 从 ZIP 存档中获取文件内容(只有一个文件,我知道它的名称)。我唯一拥有的是 ZIP 的InputStream
. 大多数示例显示如何使用此语句获取内容:
ZipFile zipFile = new ZipFile("location");
But as I said, I don't have ZIP file on my local machine and I don't want to download it. Is an InputStream
enough to read?
但正如我所说,我的本地机器上没有 ZIP 文件,我不想下载它。是InputStream
够看了?
UPD:This is how I do:
UPD:这就是我的做法:
import java.util.zip.ZipInputStream;
import com.jcraft.jsch.Channel;
import com.jcraft.jsch.ChannelSftp;
import com.jcraft.jsch.JSch;
import com.jcraft.jsch.Session;
public class SFTP {
public static void main(String[] args) {
String SFTPHOST = "host";
int SFTPPORT = 3232;
String SFTPUSER = "user";
String SFTPPASS = "mypass";
String SFTPWORKINGDIR = "/dir/work";
Session session = null;
Channel channel = null;
ChannelSftp channelSftp = null;
try {
JSch jsch = new JSch();
session = jsch.getSession(SFTPUSER, SFTPHOST, SFTPPORT);
session.setPassword(SFTPPASS);
java.util.Properties config = new java.util.Properties();
config.put("StrictHostKeyChecking", "no");
session.setConfig(config);
session.connect();
channel = session.openChannel("sftp");
channel.connect();
channelSftp = (ChannelSftp) channel;
channelSftp.cd(SFTPWORKINGDIR);
ZipInputStream stream = new ZipInputStream(channelSftp.get("file.zip"));
ZipEntry entry = zipStream.getNextEntry();
System.out.println(entry.getName); //Yes, I got its name, now I need to get content
} catch (Exception ex) {
ex.printStackTrace();
} finally {
session.disconnect();
channelSftp.disconnect();
channel.disconnect();
}
}
}
采纳答案by Tony
Well, I've done this:
嗯,我已经这样做了:
zipStream = new ZipInputStream(channelSftp.get("Port_Increment_201405261400_2251.zip"));
zipStream.getNextEntry();
sc = new Scanner(zipStream);
while (sc.hasNextLine()) {
System.out.println(sc.nextLine());
}
It helps me to read ZIP's content without writing to another file.
它可以帮助我在不写入另一个文件的情况下阅读 ZIP 的内容。
回答by Kenneth Clark
Below is a simple example on how to extract a ZIP File, you will need to check if the file is a directory. But this is the simplest.
下面是一个关于如何提取 ZIP 文件的简单示例,您需要检查该文件是否为目录。但这是最简单的。
The step you are missing is reading the input stream and writing the contents to a buffer which is written to an output stream.
您缺少的步骤是读取输入流并将内容写入写入输出流的缓冲区。
// Expands the zip file passed as argument 1, into the
// directory provided in argument 2
public static void main(String args[]) throws Exception
{
if(args.length != 2)
{
System.err.println("zipreader zipfile outputdir");
return;
}
// create a buffer to improve copy performance later.
byte[] buffer = new byte[2048];
// open the zip file stream
InputStream theFile = new FileInputStream(args[0]);
ZipInputStream stream = new ZipInputStream(theFile);
String outdir = args[1];
try
{
// now iterate through each item in the stream. The get next
// entry call will return a ZipEntry for each file in the
// stream
ZipEntry entry;
while((entry = stream.getNextEntry())!=null)
{
String s = String.format("Entry: %s len %d added %TD",
entry.getName(), entry.getSize(),
new Date(entry.getTime()));
System.out.println(s);
// Once we get the entry from the stream, the stream is
// positioned read to read the raw data, and we keep
// reading until read returns 0 or less.
String outpath = outdir + "/" + entry.getName();
FileOutputStream output = null;
try
{
output = new FileOutputStream(outpath);
int len = 0;
while ((len = stream.read(buffer)) > 0)
{
output.write(buffer, 0, len);
}
}
finally
{
// we must always close the output file
if(output!=null) output.close();
}
}
}
finally
{
// we must always close the zip file.
stream.close();
}
}
Code excerpt came from the following site:
代码摘录来自以下站点:
回答by haui
The ZipInputStream
is an InputStream
by itself and delivers the contents of each entry after each call to getNextEntry()
. Special care must be taken, not to close the stream from which the contents is read, since it is the same as the ZIP stream:
The ZipInputStream
is anInputStream
本身并在每次调用 后传送每个条目的内容getNextEntry()
。必须特别注意,不要关闭从中读取内容的流,因为它与 ZIP 流相同:
public void readZipStream(InputStream in) throws IOException {
ZipInputStream zipIn = new ZipInputStream(in);
ZipEntry entry;
while ((entry = zipIn.getNextEntry()) != null) {
System.out.println(entry.getName());
readContents(zipIn);
zipIn.closeEntry();
}
}
private void readContents(InputStream contentsIn) throws IOException {
byte contents[] = new byte[4096];
int direct;
while ((direct = contentsIn.read(contents, 0, contents.length)) >= 0) {
System.out.println("Read " + direct + "bytes content.");
}
}
When delegating reading contents to other logic, it can be necessary to wrap the ZipInputStream
with a FilterInputStream
to close only the entry instead of the whole stream as in:
将读取内容委托给其他逻辑时,可能需要ZipInputStream
用 a包装 FilterInputStream
以仅关闭条目而不是整个流,如下所示:
public void readZipStream(InputStream in) throws IOException {
ZipInputStream zipIn = new ZipInputStream(in);
ZipEntry entry;
while ((entry = zipIn.getNextEntry()) != null) {
System.out.println(entry.getName());
readContents(new FilterInputStream(zipIn) {
@Override
public void close() throws IOException {
zipIn.closeEntry();
}
});
}
}
回答by Jason Dunkelberger
OP was close. Just need to read the bytes. The call to getNextEntry positions the stream at the beginning of the entry data
(docs). If that's the entry we want (or the only entry), then the InputStream is in the right spot. All we need to do is read that entry's decompressed bytes.
OP很接近。只需要读取字节。对 getNextEntry positions the stream at the beginning of the entry data
( docs)的调用。如果这是我们想要的条目(或唯一的条目),那么 InputStream 就在正确的位置。我们需要做的就是读取该条目的解压缩字节。
byte[] bytes = new byte[(int) entry.getSize()];
int i = 0;
while (i < bytes.length) {
// .read doesn't always fill the buffer we give it.
// Keep calling it until we get all the bytes for this entry.
i += zipStream.read(bytes, i, bytes.length - i);
}
So if these bytes really are text, then we can decode those bytes to a String. I'm just assuming utf8 encoding.
因此,如果这些字节确实是文本,那么我们可以将这些字节解码为字符串。我只是假设 utf8 编码。
new String(bytes, "utf8")
Side note: I personally use apache commons-io IOUtilsto cut down on this kind of lower level stuff. The docs for ZipInputStream.read seem to imply that read will stop at the end of the current zip entry. If that is true, then reading the current textual entry is one line with IOUtils.
旁注:我个人使用 apache commons-io IOUtils来减少这种较低级别的东西。ZipInputStream.read 的文档似乎暗示 read 将在当前 zip 条目的末尾停止。如果这是真的,那么读取当前文本条目就是 IOUtils 一行。
String text = IOUtils.toString(zipStream)
回答by ThomasCh
Here a more generic solution to process a zip inputstream with a BiConsumer. It's nearly the same solution that was used by haui
这是使用 BiConsumer 处理 zip 输入流的更通用的解决方案。这几乎与 haui 使用的解决方案相同
private void readZip(InputStream is, BiConsumer<ZipEntry,InputStream> consumer) throws IOException {
try (ZipInputStream zipFile = new ZipInputStream(is);) {
ZipEntry entry;
while((entry = zipFile.getNextEntry()) != null){
consumer.accept(entry, new FilterInputStream(zipFile) {
@Override
public void close() throws IOException {
zipFile.closeEntry();
}
});
}
}
}
You can use it by just calling
您只需调用即可使用它
readZip(<some inputstream>, (entry, is) -> {
/* don't forget to close this stream after processing. */
is.read() // ... <- to read each entry
});
回答by T.KH
Unzip archive (zip) with preserving file structure into given directory. Note; this code use deps on "org.apache.commons.io.IOUtils"), but you can replace it by yours custom 'read-stream' code
将保留文件结构的存档 (zip) 解压缩到给定目录中。笔记; 此代码在“org.apache.commons.io.IOUtils”上使用 deps),但您可以将其替换为您的自定义“读取流”代码
public static void unzipDirectory(File archiveFile, File destinationDir) throws IOException
{
Path destPath = destinationDir.toPath();
try (ZipInputStream zis = new ZipInputStream(new FileInputStream(archiveFile)))
{
ZipEntry zipEntry;
while ((zipEntry = zis.getNextEntry()) != null)
{
Path resolvedPath = destPath.resolve(zipEntry.getName()).normalize();
if (!resolvedPath.startsWith(destPath))
{
throw new IOException("The requested zip-entry '" + zipEntry.getName() + "' does not belong to the requested destination");
}
if (zipEntry.isDirectory())
{
Files.createDirectories(resolvedPath);
} else
{
if(!Files.isDirectory(resolvedPath.getParent()))
{
Files.createDirectories(resolvedPath.getParent());
}
try (FileOutputStream outStream = new FileOutputStream(resolvedPath.toFile()))
{
IOUtils.copy(zis, outStream);
}
}
}
}
}