My situation is that I have a zip file that contains some files (txt, png, ...) and I want to read it directly by their names, I have tested the following code but no result (NullPointerExcepion):
InputStream in = Main.class.getResourceAsStream("/resouces/zipfile/test.txt");
BufferedReader br = new BufferedReader(new InputStreamReader(in, "UTF-8"));
resources is a package and zipfile is a zip file.
If you can be sure that your zip file will never be packed inside another jar, you can use something like:
URL zipUrl = Main.class.getResource("/resources/zipfile.zip");
URL entryUrl = new URL("jar:" + zipUrl + "!/test.txt");
InputStream is = entryUrl.openStream();
Or:
URL zipUrl = Main.class.getResource("/resources/zipfile.zip");
File zipFile = new File(zipUrl.toURI());
ZipFile zip = new ZipFile(zipFile);
InputStream is = zip.getInputStream(zip.getEntry("test.txt"));
Otherwise, your choices are:
Use a ZipInputStream to scan through the zip file once for each entry that you need to load. This may be slow if you have a lot of resources, unless you can reuse the same ZipInputStream for all your resources.
Don't pack the resources in a nested zip file, and just inline them in the jar with the code.
Copy the nested zip file into a temporary directory, and access it using the ZipFile class.
Your current approach is definitely not going to work. You made up an arbitrary 'access' scheme and used it in a class that has no idea what you are trying to do. What you can do is use a ZipInputStream to read the entry you are looking for:
URL zipFileURL = Thread.currentThread().getContextClassLoader().getResource("zipfile.zip");
InputStream inputStream = zipFileURL.openStream();
ZipInputStream zipInputStream = new ZipInputStream(inputStream);
ZipEntry zipEntry = null;
do {
zipEntry = zipInputStream.getNextEntry();
if(zipEntry == null) break;
}
while(zipEntry != null && (! "textfile".equals(zipEntry.getName()));
if(zipEntry != null ) {
// do some stuff
}
This is adhoc code, fix it up to do what you need. Also, there might be some more efficient classes to handle Zip files, for example in the Apache Commons IO library.
What is the path of the test.txt within the zip file? You need to use the path within the zip file to read this file. Also make sure that your zipfile is in the classpath. In fact, you can bundle this in a jar file.
Related
I have a situation where I need to scan the runtime classpath for a resource file (say, res/config/meta.cfg), and then create a File handle for it. The best I've been able to come up with is:
// This file is located inside a JAR that is on the runtime classpath.
String fileName = "res/config/meta.cfg";
try {
InputStream inStream = ClassLoader.getSystemResourceAsStream(fileName);
File file = new File(String.format("${java.io.tmpdir}/%s", fileName));
FileOutputStream foutStream = null;
foutStream = new FileOutputStream(file);
int read = 0;
byte[] bytes = new byte[1024];
while((read = inStream.read(bytes)) != -1)
foutStream.write(bytes, 0, read);
foutStream.close();
return file;
} catch (Exception exc) {
throw new RuntimeException(exc);
}
So essentially, read in the resource as an InputStream, and then write the stream to a temp file (under {$java.io.tmpdir}) so that we can obtain a valid File handle for it.
This seems like going 3 sides around the barn. Is there a better/easier/more elegant way of doing this? Thanks in advance!
No.
Of course you can (and probably should) use a library to copy the InputStream's content to a file but that obviously is not the point of your question.
The classpath does not consist of directories only; resources can be inside archives (typically JARs) or on servers, and may not exist as something that can be accessed via a java.io.File object.
Typically the core problem is to use java.io.File objects where an InputStream would be sufficient. Sometimes you can't do anything against it when using a third-party library but it is a hint that the library designers didn't work very carefully. If you need the file handle in your own code you should have another look why it can't be an InputStream. Most of the time it can.
I am experiencing a strange behavior with java.util.zip.*
I have a zip file and upon decompressing follwing tihngs happen
ZipFile zipfile = new ZipFile(file, ZipFile.OPEN_READ);
This is exaxt error message
java.util.zip.ZipException: error in opening zip file
at java.util.zip.ZipFile.open(Native Method)
at java.util.zip.ZipFile.<init>(ZipFile.java:127)
at java.util.zip.ZipFile.<init>(ZipFile.java:143)
at com.basware.ExtractZip.unpack(ExtractZip.java:27)
at com.basware.ExtractZip.main(ExtractZip.java:17)
But if I use the following code it is able to open the archive without any errors
try {
BufferedOutputStream dest = null;
File file = new File("File_Path");
FileInputStream fis = new FileInputStream(file);
ZipInputStream zis = new ZipInputStream(new BufferedInputStream(fis));
ZipEntry entry;
while((entry = zis.getNextEntry()) != null) {
System.out.println("Extracting: " +entry);
int count;
byte data[] = new byte[BUFFER];
// write the files to the disk
FileOutputStream fos = new
FileOutputStream(entry.getName());
dest = new
BufferedOutputStream(fos, BUFFER);
while ((count = zis.read(data, 0, BUFFER))
!= -1) {
dest.write(data, 0, count);
}
dest.flush();
dest.close();
}
zis.close();
Please note that files are compressed using WinZIP.
My question is as ZipFile and ZipInputStream are almost same ,why ZipFile is giving exception and why it is unable to perform decompression.
EDIT : The problem is if I zip the file using WinZip tool and then decompress it using listed program it is working fine.But, this problem is specifically coming for archives coming from external source(external source claims that they are using WinZip).On top of it, if I open the very same archive(external one) using WinZip tool it is showing and decompressing files.But this JAVA specific code(ZipFile) is not working at all.
EDIT: I am not able to figure it out why java native code is not working for my ZIP archives, but apache compress solved my problem.It is working for me as suggested by Ian Roberts.
ZipFile attempts to parse the "central directory" at the end of the zip in order to build up a data structure that allows you to access individual entries by name. ZipInputStream doesn't, it only looks at the local header of each entry as it reads through the file from top to bottom. So it looks like your file has good entries but a corrupted central directory for some reason.
There are a number of possibilities, for example issues with the encoding of non-ASCII characters in entry names, or if the zip has more than 64k entries. I would try the commons-compress implementation of ZipFile - even if it doesn't work it should give you a more specific error message than the "something is wrong" that you get from java.util.zip.
In addition to Ian Robert's answer, if Java 7 is an option, you may wish to sidestep the older java.util.zip libraries in favor of using the ZIP filesystem provider.
I tried the java.util.zip package, it is too slow.
Then I found LZMA SDK and 7z jbinding but they are also lacking something.
The LZMA SDK does not provide a kind of documentation/tutorial of how-to-use, it is very frustrating. No javadoc.
While the 7z jbinding does not provide a simple way to extract only 1 file, however, it only provide way to extract all the content of the zip file. Moreover, it does not provide a way to specify a location to place the unzipped file.
Any idea please?
What does your code with java.util.zip look like and how big of a zip file are you dealing with?
I'm able to extract a 4MB entry out of a 200MB zip file with 1,800 entries in roughly a second with this:
OutputStream out = new FileOutputStream("your.file");
FileInputStream fin = new FileInputStream("your.zip");
BufferedInputStream bin = new BufferedInputStream(fin);
ZipInputStream zin = new ZipInputStream(bin);
ZipEntry ze = null;
while ((ze = zin.getNextEntry()) != null) {
if (ze.getName().equals("your.file")) {
byte[] buffer = new byte[8192];
int len;
while ((len = zin.read(buffer)) != -1) {
out.write(buffer, 0, len);
}
out.close();
break;
}
}
I have not benchmarked the speed but with java 7 or greater, I extract a file as follows.
I would imagine that it's faster than the ZipFile API:
A short example extracting META-INF/MANIFEST.MF from a zip file test.zip:
// file to extract from zip file
String file = "MANIFEST.MF";
// location to extract the file to
File outputLocation = new File("D:/temp/", file);
// path to the zip file
Path zipFile = Paths.get("D:/temp/test.zip");
// load zip file as filesystem
try (FileSystem fileSystem = FileSystems.newFileSystem(zipFile)) {
// copy file from zip file to output location
Path source = fileSystem.getPath("META-INF/" + file);
Files.copy(source, outputLocation.toPath());
}
Use a ZipFile rather than a ZipInputStream.
Although the documentation does not indicate this (it's in the docs for JarFile), it should use random-access file operations to read the file. Since a ZIPfile contains a directory at a known location, this means a LOT less IO has to happen to find a particular file.
Some caveats: to the best of my knowledge, the Sun implementation uses a memory-mapped file. This means that your virtual address space has to be large enough to hold the file as well as everything else in your JVM. Which may be a problem for a 32-bit server. On the other hand, it may be smart enough to avoid memory-mapping on 32-bit, or memory-map just the directory; I haven't tried.
Also, if you're using multiple files, be sure to use a try/finally to ensure that the file is closed after use.
I'm having problems setting the path of the zip file, X, in ZipFile zipfile = new ZipFile("X");.
I don't want to hardcode the path such that it becomes ZipFile zipfile = new ZipFile("C:/docs/data.zip");.
I want to do something like :
ZipFile zipfile = new ZipFile(getServletContext().getResourceAsStream("/WEB-INF/" + request.getAttribute("myFile").toString());
Where the path of the zip file is determined by the selection of the user. But, this gives an error, because this only works for InputStream.
Previously, I've already retrieved the multipart/form data and gotten the real path of the zip file:
String path = getServletContext().getRealPath("/WEB-INF");
UploadBean bean = new UploadBean();
bean.setFolderstore(path);
MultipartFormDataRequest multiPartRequest = new MultipartFormDataRequest(request);
bean.store(multiPartRequest); //store in WEB-INF
// get real path / name of zip file which is store in the WEB-INF
Hashtable files = multiPartRequest.getFiles();
UploadFile upFile = (UploadFile) files.get("file");
if (upFile != null) request.setAttribute("myFile", upFile.getFileName());
Any solutions to this?
You can convert webcontent-relative paths to absolute disk file system paths in two ways:
Just use ServletContext#getRealPath() as you previously already did.
ZipFile zipfile = new ZipFile(getServletContext().getRealPath("/WEB-INF/" + request.getAttribute("myFile").toString()));
Use ServletContext#getResource() instead. It returns an URL. Call getPath() on it.
ZipFile zipfile = new ZipFile(getServletContext().getResource("/WEB-INF/" + request.getAttribute("myFile").toString()).getPath());
Way #1 is preferred.
I don't understand why you don't use the real path that you already have.
Anyway, you can work with a ZipInputStream.
This way you can handle your file as a simple Stream. The only big differences are the getName() method and size() that you can't directly access. With a ZIS you will be able to read every entry.
Resources :
Javadoc - ZipInputStream
I am trying to zip the following file structure on my machine,
parent/
parent/test1
parent/test1/image1.jpeg
parent/test2
The problem here is i cant zip the above file structure using java. I have google and found following code sample but it only zip the files only inside a given folder.
File inFolder=new File("out");
File outFolder=new File("Out.zip");
ZipOutputStream out = new ZipOutputStream(new
BufferedOutputStream(new FileOutputStream(outFolder)));
BufferedInputStream in = null;
byte[] data = new byte[1000];
String files[] = inFolder.list();
for (int i=0; i<files.length; i++)
{
in = new BufferedInputStream(new FileInputStream
(inFolder.getPath() + "/" + files[i]), 1000);
out.putNextEntry(new ZipEntry(files[i]));
int count;
while((count = in.read(data,0,1000)) != -1)
{
out.write(data, 0, count);
}
out.closeEntry();
}
out.flush();
out.close();
In the above code the out is a folder and we need to have some files..also folder cannot be empty if so it throws a exception java.util.zip.ZipException or cant contain any sub folders even files inside it (eg:out\newfolder\image.jpeg) if so it throws a java.io.FileNotFoundException: out\newfolder (Access is denied).
In my case im costructig the above file structure by quering the database sometime empty folders along the folder structure can be have.
Can some one please tell me a solution?
Thank You.
What is probably happening is that you're trying to treat every entry as a FileInputStream. However, for a directory, this is not true. Since the path is not to a file, when you try to read it, a FileNotFoundException is thrown. For directories, you still want to create the ZipEntry, but instead of trying to read in any data, just skip it and move on to the next path.
write two methods. The first one takes dirpath, makes a zip stream and calls another method which copies files to the zip stream and calls itself recursively for directories as below:
open an entry in the zip stream for the given directory
list files and dirs in the given directory, loop through them
if an entry is a file, open an entry, copy file content to the entry, close it
if an entry is a directory, call this method. Pass the zip stream
close the entry.
The first method closes the zip stream.