Как читать файл из ZIP с помощью InputStream?
Я должен получить содержимое файла из ZIP-архива (только один файл, я знаю его имя), используя SFTP. Единственное, что у меня есть, - ZIP InputStream
. В большинстве примеров показано, как получить контент, используя этот оператор:
ZipFile zipFile = new ZipFile("location");
Но, как я уже сказал, у меня нет ZIP файла на моей локальной машине, и я не хочу его загружать. Достаточно ли читать InputStream
?
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();
}
}
}
Ответы
Ответ 1
Хорошо, я сделал это:
zipStream = new ZipInputStream(channelSftp.get("Port_Increment_201405261400_2251.zip"));
zipStream.getNextEntry();
sc = new Scanner(zipStream);
while (sc.hasNextLine()) {
System.out.println(sc.nextLine());
}
Он помогает мне читать ZIP-содержимое без записи в другой файл.
Ответ 2
Ниже приведен простой пример того, как извлечь ZIP файл, вам нужно проверить, является ли файл каталогом. Но это самое простое.
Шаг, который вы пропускаете, - это чтение входного потока и запись содержимого в буфер, который записывается в выходной поток.
// 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();
}
}
Отрывок кода пришел со следующего сайта:
http://www.thecoderscorner.com/team-blog/java-and-jvm/12-reading-a-zip-file-from-java-using-zipinputstream#.U4RAxYamixR
Ответ 3
ZipInputStream
сам по себе является InputStream
и доставляет содержимое каждой записи после каждого вызова 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.");
}
}
При делегировании содержимого чтения другой логике может потребоваться обернуть ZipInputStream
с 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();
}
});
}
}
Ответ 4
ОП был близок. Просто нужно прочитать байты. Вызов getNextEntry positions the stream at the beginning of the entry data
(документов). Если эта запись нам нужна (или единственная запись), то 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);
}
Поэтому, если эти байты действительно являются текстом, мы можем декодировать эти байты в строку. Я просто предполагаю, что кодировка utf8.
new String(bytes, "utf8")
Примечание: я лично использую Apache commons-io IOUtils, чтобы сократить этот тип вещей более низкого уровня. Документы для ZipInputStream.read, кажется, подразумевают, что чтение остановится в конце текущей записи zip. Если это правда, тогда чтение текущей текстовой записи - это одна строка с IOUtils.
String text = IOUtils.toString(zipStream)
Ответ 5
Здесь более общее решение для обработки потока входных данных с помощью BiConsumer. Это почти то же решение, которое использовалось 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();
}
});
}
}
}
Вы можете использовать его, просто позвонив
readZip(<some inputstream>, (entry, is) -> {
/* don't forget to close this stream after processing. */
is.read() // ... <- to read each entry
});