Enumerating FTPFile from FTP using ZipFile - java

My usual approach without an FTP to read ZipFiles was the following:
private void getLogFromZip(File logZip){
ZipFile zf = new ZipFile(logZip);
Enumeration<?> entries = zf.entries();
while (entries.hasMoreElements()) {
ZipEntry ze = (ZipEntry) entries.nextElement();
//do something with entry
}
Now that I have a connection to an FTP server, retrieving an FTPFile and using it makes things difficult:
private void getLogFromZip(FTPFile logZip){
ZipFile zf = new ZipFile(logZip.getName()); //here's the problem
Enumeration<?> entries = zf.entries();
while (entries.hasMoreElements()) {
ZipEntry ze = (ZipEntry) entries.nextElement();
//do something with entry
}
Directly in line 1 I am getting this:
java.io.FileNotFoundException: logger_150510_092333.zip (System cannot find the file? specified)
What is a workaround for that? How can I specify a path so that it knows where to look for the zip?
Many thanks in advance!

You probably have to get the file from the FTP server first, before you can access it. The FTPFile instance appears to be only a link to the actual file.
Have a look here for an example: http://www.mysamplecode.com/2012/03/apache-commons-ftpclient-java-example_16.html

Related

Getting specific file from ZipInputStream

I can go through ZipInputStream, but before starting the iteration I want to get a specific file that I need during the iteration. How can I do that?
ZipInputStream zin = new ZipInputStream(myInputStream)
while ((entry = zin.getNextEntry()) != null)
{
println entry.getName()
}
If the myInputStream you're working with comes from a real file on disk then you can simply use java.util.zip.ZipFile instead, which is backed by a RandomAccessFile and provides direct access to the zip entries by name. But if all you have is an InputStream (e.g. if you're processing the stream directly on receipt from a network socket or similar) then you'll have to do your own buffering.
You could copy the stream to a temporary file, then open that file using ZipFile, or if you know the maximum size of the data in advance (e.g. for an HTTP request that declares its Content-Length up front) you could use a BufferedInputStream to buffer it in memory until you've found the required entry.
BufferedInputStream bufIn = new BufferedInputStream(myInputStream);
bufIn.mark(contentLength);
ZipInputStream zipIn = new ZipInputStream(bufIn);
boolean foundSpecial = false;
while ((entry = zin.getNextEntry()) != null) {
if("special.txt".equals(entry.getName())) {
// do whatever you need with the special entry
foundSpecial = true;
break;
}
}
if(foundSpecial) {
// rewind
bufIn.reset();
zipIn = new ZipInputStream(bufIn);
// ....
}
(I haven't tested this code myself, you may find it's necessary to use something like the commons-io CloseShieldInputStream in between the bufIn and the first zipIn, to allow the first zip stream to close without closing the underlying bufIn before you've rewound it).
use the getName() method on ZipEntry to get the file you want.
ZipInputStream zin = new ZipInputStream(myInputStream)
String myFile = "foo.txt";
while ((entry = zin.getNextEntry()) != null)
{
if (entry.getName().equals(myFileName)) {
// process your file
// stop looking for your file - you've already found it
break;
}
}
From Java 7 onwards, you are better off using ZipFile instead of ZipStream if you only want one file and you have a file to read from:
ZipFile zfile = new ZipFile(aFile);
String myFile = "foo.txt";
ZipEntry entry = zfile.getEntry(myFile);
if (entry) {
// process your file
}
Look at Finding a file in zip entry
ZipFile file = new ZipFile("file.zip");
ZipInputStream zis = searchImage("foo.png", file);
public searchImage(String name, ZipFile file)
{
for (ZipEntry e : file.entries){
if (e.getName().endsWith(name)){
return file.getInputStream(e);
}
}
return null;
}
I'm late to the party, but all above "answers" does not answer the question and accepted "answer" suggest create temp file which is inefficient.
Lets create sample zip file:
seq 10000 | sed "s/^.*$/a/"> /tmp/a
seq 10000 20000 | sed "s/^.*$/b/"> /tmp/b
seq 20000 30000 | sed "s/^.*$/c/"> /tmp/c
zip /tmp/out.zip /tmp/a /tmp/b /tmp/c
so now we have /tmp/out.zip file, which contains 3 files, each of them full of chars a, b or c.
Now lets read it:
public static void main(String[] args) throws IOException {
ZipInputStream zipStream = new ZipInputStream(new FileInputStream("/tmp/out.zip"));
ZipEntry zipEntry;
while ((zipEntry = zipStream.getNextEntry()) != null) {
String name = zipEntry.getName();
System.out.println("Entry: "+name);
if (name.equals("tmp/c")) {
byte[] bytes = zipStream.readAllBytes();
String s = new String(bytes);
System.out.println(s);
}
}
}
method readAllBytes seems weird, while we're in processing of stream, but it seems to work, I tested it also on some images, where there is higher chance of failure. So it's probably just unintuitive api, but it seems to work.

getNextEntry() doesn't display folder as an entry?

Hi I'm new to android programming.
I'm trying to create a program to unzip a zipped file in my sd card and I noticed something when I debug.
public void testZipOrder() throws Exception {
File file = new File(_zipFile);
zis = new ZipInputStream(new FileInputStream(file));
ZipEntry entry = null;
while ( (entry = zis.getNextEntry()) != null ) {
System.out.println( entry.getName());
}
}
}
this give me an output of :
06-27 00:42:06.360: I/System.out(15402): weee.txt
06-27 00:42:06.360: I/System.out(15402): hi/bye.txt
06-27 00:42:06.360: I/System.out(15402): hi/hiwayne.txt
isn't it suppose to give
weee.txt
hi/
hi/bye.txt
hi/hiwayne.txt
or something that displays its folder instead?
I tried this on my own environment using a test zip file created with 7zip and the following method:
public void testZipOrder() throws Exception {
File file = new File("zip.zip");
ZipInputStream zis = new ZipInputStream(new FileInputStream(file));
ZipEntry entry = null;
while ( (entry = zis.getNextEntry()) != null ) {
System.out.println( entry.getName());
}
zis.close();
}
Note this method is effectively identical to yours.
The resulting output was:
file1.txt
folder1/
folder1/file2.txt
folder1/folder2/
folder1/folder2/file3.txt
Which is, I believe, what you are looking for. As such I expect the problem is with the zip file itself, not your code. It is likely that your zip file does not contain an entry for the directory "hi/".
See here for a basic description of how zip files are structured.
ZIP spec does not require the ordered "placement" of the file and its parent(s) directory in the zip file, and in fact the parent directory entries can be totally absent
See https://bugs.openjdk.java.net/browse/JDK-8054027

How does ZipInputStream.getNextEntry() work?

Say we have code like:
File file = new File("zip1.zip");
ZipInputStream zis = new ZipInputStream(new FileInputStream(file));
Let's assume you have a .zip file that contains the following:
zip1.zip
hello.c
world.java
folder1
foo.c
bar.java
foobar.c
How would zis.getNextEntry() iterate through that?
Would it return hello.c, world.java, folder1, foobar.c and completely ignore the files in folder1?
Or would it return hello.c, world.java, folder1, foo.c, bar.java, and then foobar.c?
Would it even return folder1 since it's technically a folder and not a file?
Thanks!
Well... Lets see:
ZipInputStream zis = new ZipInputStream(new FileInputStream("C:\\New Folder.zip"));
try
{
ZipEntry temp = null;
while ( (temp = zis.getNextEntry()) != null )
{
System.out.println( temp.getName());
}
}
Output:
New Folder/
New Folder/folder1/
New Folder/folder1/bar.java
New Folder/folder1/foo.c
New Folder/foobar.c
New Folder/hello.c
New Folder/world.java
Yes. It will print the folder name too, since it's also an entry within the zip. It will also print in the same order as it is displayed inside the zip. You can use below test to verify your output.
public class TestZipOrder {
#Test
public void testZipOrder() throws Exception {
File file = new File("/Project/test.zip");
ZipInputStream zis = new ZipInputStream(new FileInputStream(file));
ZipEntry entry = null;
while ( (entry = zis.getNextEntry()) != null ) {
System.out.println( entry.getName());
}
}
}
Excerpt from: https://blogs.oracle.com/CoreJavaTechTips/entry/creating_zip_and_jar_files
java.util.zip libraries offer some level of control for the added entries of the ZipOutputStream.
First, the order you add entries to the ZipOutputStream is the order they are physically located in the .zip file.
You can manipulate the enumeration of entries returned back by the entries() method of ZipFile to produce a list in alphabetical or size order, but the entries are still stored in the order they were written to the output stream.
So I would believe that you have to use the entries() method to see the order in which it will be iterated through.
ZipFile zf = new ZipFile("your file path with file name");
for (Enumeration<? extends ZipEntry> e = zf.entries();
e.hasMoreElements();) {
System.out.println(e.nextElement().getName());
}
The zip file internal directory is a "flat" list of all the files and directories in the zip. getNextEntry will iterate through the list and sequentially identify every file and directory in the zip file.
There is a variant of the zip file format that has no central directory, in which case (if it's handled at all) I suspect you'd iterate through all actual files in the zip, skipping directories (but not skipping files in directories).

How do I enumerate the content of a zipped folder in Java?

ZipeFile file = new ZipFile(filename);
ZipEntry folder = this.file.getEntry("some/path/in/zip/");
if (folder == null || !folder.isDirectory())
throw new Exception();
// now, how do I enumerate the contents of the zipped folder?
It doesn't look like there's a way to enumerate ZipEntry under a certain directory.
You'd have to go through all ZipFile.entries() and filter the ones you want based on the ZipEntry.getName() and see if it String.startsWith(String prefix).
String specificPath = "some/path/in/zip/";
ZipFile zipFile = new ZipFile(file);
Enumeration<? extends ZipEntry> entries = zipFile.entries();
while (entries.hasMoreElements()) {
ZipEntry ze = entries.nextElement();
if (ze.getName().startsWith(specificPath)) {
System.out.println(ze);
}
}
You don't - at least, not directly. ZIP files are not actually hierarchical. Enumerate all the entries (via ZipFile.entries() or ZipInputStream.getNextEntry()) and determine which are within the folder you want by examining the name.
Can you just use entries()? API link

Unzipping a file from InputStream and returning another InputStream

I am trying to write a function which will accept an InputStream with zipped file data and would return another InputStream with unzipped data.
The zipped file will only contain a single file and thus there is no requirement of creating directories, etc...
I tried looking at ZipInputStream and others but I am confused by so many different types of streams in Java.
Concepts
GZIPInputStream is for streams (or files) zipped as gzip (".gz" extension). It doesn't have any header information.
This class implements a stream filter for reading compressed data in the GZIP file format
If you have a real zip file, you have to use ZipFile to open the file, ask for the list of files (one in your example) and ask for the decompressed input stream.
Your method, if you have the file, would be something like:
// ITS PSEUDOCODE!!
private InputStream extractOnlyFile(String path) {
ZipFile zf = new ZipFile(path);
Enumeration e = zf.entries();
ZipEntry entry = (ZipEntry) e.nextElement(); // your only file
return zf.getInputStream(entry);
}
Reading an InputStream with the content of a .zip file
Ok, if you have an InputStream you can use (as #cletus says) ZipInputStream. It reads a stream including header data.
ZipInputStream is for a stream with [header information + zippeddata]
Important: if you have the file in your PC you can use ZipFile class to access it randomly
This is a sample of reading a zip-file through an InputStream:
import java.io.FileInputStream;
import java.util.zip.ZipEntry;
import java.util.zip.ZipInputStream;
public class Main {
public static void main(String[] args) throws Exception
{
FileInputStream fis = new FileInputStream("c:/inas400.zip");
// this is where you start, with an InputStream containing the bytes from the zip file
ZipInputStream zis = new ZipInputStream(fis);
ZipEntry entry;
// while there are entries I process them
while ((entry = zis.getNextEntry()) != null)
{
System.out.println("entry: " + entry.getName() + ", " + entry.getSize());
// consume all the data from this entry
while (zis.available() > 0)
zis.read();
// I could close the entry, but getNextEntry does it automatically
// zis.closeEntry()
}
}
}
If you can change the input data I would suggested you to use GZIPInputStream.
GZipInputStream is different from ZipInputStream since you only have one data inside it. So the whole input stream represents the whole file. In ZipInputStream the whole stream contains also the structure of the file(s) inside it, which can be many.
It is on scala syntax:
def unzipByteArray(input: Array[Byte]): String = {
val zipInputStream = new ZipInputStream(new ByteArrayInputStream(input))
val entry = zipInputStream.getNextEntry
IOUtils.toString(zipInputStream, StandardCharsets.UTF_8)
}
Unless I'm missing something, you should absolutely try and get ZipInputStream to work and there's no reason it shouldn't (I've certainly used it on several occasions).
What you should do is try and get ZipInputStream to work and if you can't, post the code and we'll help you with whatever problems you're having.
Whatever you do though, don't try and reinvent its functionality.

Categories

Resources