Is there a direct way to unpack a java.util.zip.ZipEntry to a File?
I want to specify a location (like "C:\temp\myfile.java") and unpack the Entry to that location.
There is some code with streams on the net, but I would prefer a tested library function.
Use ZipFile class
ZipFile zf = new ZipFile("zipfile");
Get entry
ZipEntry e = zf.getEntry("name");
Get inpustream
InputStream is = zf.getInputStream(e);
Save bytes
Files.copy(is, Paths.get("C:\\temp\\myfile.java"));
Use the below code to extract the "zip file" into File's then added in the list using ZipEntry. Hopefully, this will help you.
private List<File> unzip(Resource resource) {
List<File> files = new ArrayList<>();
try {
ZipInputStream zin = new ZipInputStream(resource.getInputStream());
ZipEntry entry = null;
while((entry = zin.getNextEntry()) != null) {
File file = new File(entry.getName());
FileOutputStream os = new FileOutputStream(file);
for (int c = zin.read(); c != -1; c = zin.read()) {
os.write(c);
}
os.close();
files.add(file);
}
} catch (IOException e) {
log.error("Error while extract the zip: "+e);
}
return files;
}
Use ZipInputStream to move to the desired ZipEntry by iterating using the getNextEntry() method. Then use the ZipInputStream.read(...) method to read the bytes for the current ZipEntry. Output those bytes to a FileOutputStream pointing to a file of your choice.
Related
I have the following situation:
I am getting a Zip File sent over the network in the Form of a Byte Array. I don't want to save that file locally, but instead target an individual file in that archive and import it into my solution as an InputStream.
Thus far, I have managed to get to the point where I can identify the correct ZipEntry. However, from here all the Zip-tutorials continue by reading the entry by its file name, which naturally does not work in my case since the zip file does not exist locally.
private void InputStream getReqifInputStreamFrom(byte[] reqifzFileBytes){
ByteArrayInputStream inputStream = new ByteArrayInputStream(reqifzFileBytes);
ZipInputStream zipInputStream = new ZipInputStream(inputStream);
ZipEntry zipEntry = zipInputStream.getNextEntry();
while (zipEntry != null) {
String fileName = zipEntry.getName();
if (fileName.endsWith(REQIF_FILE_SUFFIX)) {
return ???;
}
zipEntry = zipInputStream.getNextEntry();
}
}
So, what can I do at that point to return an InputStream that represents exactly the one file that the ZipEntry represents at that point? I have considered just returning the zipInputStream, but since I don't know exactly how that one works I'm afraid that doing so would also include all files that are still in the stream after that file to be returned as well.
You're virtually there. Try
private InputStream getReqifInputStreamFrom(byte[] reqifzFileBytes) throws IOException {
InputStream result = null;
ByteArrayInputStream inputStream = new ByteArrayInputStream(reqifzFileBytes);
ZipInputStream zipInputStream = new ZipInputStream(inputStream);
ZipEntry zipEntry = zipInputStream.getNextEntry();
while (zipEntry != null) {
String fileName = zipEntry.getName();
if (fileName.endsWith(REQIF_FILE_SUFFIX)) {
result = zipInputStream;
break;
}
zipEntry = zipInputStream.getNextEntry();
}
return result;
}
Unzipping multipart file with and without subdirectories since I am not giving any instruction to user to how to zip the file therefore I need to find/search all files from Zip file which might have directories and subdirectories and keeping all files in a seperate different folder.
So basically it is some kind of smart unzipping where it detects the directory using ZipEntry then skip and find file to write in the folder.
I have written a code but I am not even near it since I am getting only one file that too which has no directories in it.
String outputPath="C:\\Users\\Plootus\\exceldocs\\";
FileSystem fileSystem = FileSystems.getDefault();
try
{
ZipInputStream zis=new ZipInputStream(serverFile.getInputStream());
BufferedInputStream bis=null;
InputStream is=null;
//Get file entries
ZipEntry entry=null;
//We will unzip files in this folder
while ( (entry = zis.getNextEntry()) != null ) {
System.out.println(entry.getName());
if(!entry.isDirectory()) {
System.out.println(entry.getName());
is = zis;
bis = new BufferedInputStream(is);
String uncompressedFileName = outputPath+toolName+entry.getName();
Path uncompressedFilePath = fileSystem.getPath(uncompressedFileName);
Files.createFile(uncompressedFilePath);
FileOutputStream fileOutput = new FileOutputStream(uncompressedFileName);
while (bis.available() > 0)
{
fileOutput.write(bis.read());
}
fileOutput.close();
System.out.println("Written :" + entry.getName());
bis.close();
is.close();
}
}
zis.close();
return true;
}
catch(IOException e)
{
return false;
}
return false;
Objective: Zip file contains possible entries
1.) abc.zip(Multipart File)
-folder1-arkan.csv,dan.csv,kud.csv
abc.zip(Mutlipart File)
-folder1--bio.csv(file)-folder-2(inside folder1)-arkan.csv,dan.csv,kud.csv
abc.zip(Mutlipart File)
-arkan.csv,dan.csv,kud.csv
Instead of extracting from MultipartFile and handling entries as ZipEntry(as told by #Jokkeri) is not possible therefore I found other way to do it.
I will save that file and when the operation is done then delete it.
After receiving multipart file I saved the file using File object(saveZip)
try(ZipFile file = new ZipFile(saveZip.getCanonicalPath()))
{
FileSystem fileSystem = FileSystems.getDefault();
//Get file entries
Path inputpath=fileSystem.getPath(file.getName());
Enumeration<? extends ZipEntry> entries = file.entries();
//We will unzip files in this folder
File directory=new File(zipFilePath.concat(username+"-"+toolName));
if(!directory.exists()) {
directory.mkdir();
}
//Iterate over entries
while (entries.hasMoreElements())
{
ZipEntry entry = entries.nextElement();
String abc[]=entry.getName().split("/");
//Else create the file
if(!entry.isDirectory())
{
InputStream is = file.getInputStream(entry);
BufferedInputStream bis = new BufferedInputStream(is);
String uncompressedFileName = zipFilePath +username+"-"+toolName+"/"+ abc[abc.length-1];
Path uncompressedFilePath = fileSystem.getPath(uncompressedFileName);
if(Files.notExists(uncompressedFilePath))
Files.createFile(uncompressedFilePath);
FileOutputStream fileOutput = new FileOutputStream(uncompressedFileName);
while (bis.available() > 0)
{
fileOutput.write(bis.read());
}
fileOutput.close();
System.out.println("Written :" + entry.getName());
is.close();
bis.close();
}
}
file.close();
Files.deleteIfExists(inputpath);
return true;
}catch(IOException e)
{
e.printStackTrace();
return false;
}
I'm trying to read .srt files that are located in zip file itself located in a zip file. I succeed to read .srt files that were in a simple zip with the extract of code below :
for (Enumeration enume = fis.entries(); enume.hasMoreElements();) {
ZipEntry entry = (ZipEntry) enume.nextElement();
fileName = entry.toString().substring(0,entry.toString().length()-4);
try {
InputStream in = fis.getInputStream(entry);
BufferedReader reader = new BufferedReader(new InputStreamReader(in));
String ext = entry.toString().substring(entry.toString().length()-4, entry.toString().length());
But now i don't know how i could get to the zip file inside the zip file.
I tried using ZipFile fis = new ZipFile(filePath) with filePath being the path of the zip file + the name of zip file inside. It didn't recognize the path so i don't know if i am clear.
Thanks.
ZipFile only works with real files, because it's intended for use as a random access mechanism which needs to be able to seek directly to specific locations in the file to read entries by name. But as VGR suggests in the comments, while you can't get random access to the zip-inside-a-zip you can use ZipInputStream, which provides strictly sequential access to the entries and works with any InputStream of zip-format data.
However, ZipInputStream has a slightly odd usage pattern compared to other streams - calling getNextEntry reads the entry metadata and positions the stream to read that entry's data, you read from the ZipInputStream until it reports EOF, then you (optionally) call closeEntry() before moving on to the next entry in the stream.
The critical point is that you must not close() the ZipInputStream until you have finished reading the final entry, so depending what you want to do with the entry data you might need to use something like the commons-io CloseShieldInputStream to guard against the stream getting closed prematurely.
try(ZipInputStream outerZip = new ZipInputStream(fis)) {
ZipEntry outerEntry = null;
while((outerEntry = outerZip.getNextEntry()) != null) {
if(outerEntry.getName().endsWith(".zip")) {
try(ZipInputStream innerZip = new ZipInputStream(
new CloseShieldInputStream(outerZip))) {
ZipEntry innerEntry = null;
while((innerEntry = innerZip.getNextEntry()) != null) {
if(innerEntry.getName().endsWith(".srt")) {
// read the data from the innerZip stream
}
}
}
}
}
}
Find the code to extract .zip files recursively:
public void extractFolder(String zipFile) throws ZipException, IOException {
System.out.println(zipFile);
int BUFFER = 2048;
File file = new File(zipFile);
ZipFile zip = new ZipFile(file);
String newPath = zipFile.substring(0, zipFile.length() - 4);
new File(newPath).mkdir();
Enumeration zipFileEntries = zip.entries();
// Process each entry
while (zipFileEntries.hasMoreElements())
{
// grab a zip file entry
ZipEntry entry = (ZipEntry) zipFileEntries.nextElement();
String currentEntry = entry.getName();
File destFile = new File(newPath, currentEntry);
//destFile = new File(newPath, destFile.getName());
File destinationParent = destFile.getParentFile();
// create the parent directory structure if needed
destinationParent.mkdirs();
if (!entry.isDirectory())
{
BufferedInputStream is = new BufferedInputStream(zip
.getInputStream(entry));
int currentByte;
// establish buffer for writing file
byte data[] = new byte[BUFFER];
// write the current file to disk
FileOutputStream fos = new FileOutputStream(destFile);
BufferedOutputStream dest = new BufferedOutputStream(fos,
BUFFER);
// read and write until last byte is encountered
while ((currentByte = is.read(data, 0, BUFFER)) != -1) {
dest.write(data, 0, currentByte);
}
dest.flush();
dest.close();
is.close();
}
if (currentEntry.endsWith(".zip"))
{
// found a zip file, try to open
extractFolder(destFile.getAbsolutePath());
}
}
}
Say I have a zip file MyZipFile.zip which contains (1) a file MyFile.txt and (2) a folder MyFolder which contains a file MyFileInMyFolder.txt, i.e. something as follows:
MyZipFile.zip
|-> MyFile.txt
|-> MyFolder
|-> MyFileInMyFolder.txt
I want to decompress this zip archive. The most common code sample I have been able to find searching online uses the ZipInputStream class somewhat like the code pasted at the bottom of this question. The problem with this however, using the example above, is that it will create MyFolder but will not decompress the contents of MyFolder. Anyone know whether it is possible to decompress the contents of a folder in a zip archive using ZipInputStream or by any other means?
public static boolean unzip(File sourceZipFile, File targetFolder)
{
// pre-stuff
ZipInputStream zipInputStream = new ZipInputStream(new FileInputStream(sourceZipFile));
ZipEntry zipEntry = null;
while ((zipEntry = zipInputStream.getNextEntry()) != null)
{
File zipEntryFile = new File(targetFolder, zipEntry.getName());
if (zipEntry.isDirectory())
{
if (!zipEntryFile.exists() && !zipEntryFile.mkdirs())
return false;
}
else
{
FileOutputStream fileOutputStream = new FileOutputStream(zipEntryFile);
byte buffer[] = new byte[1024];
int count;
while ((count = zipInputStream.read(buffer, 0, buffer.length)) != -1)
fileOutputStream.write(buffer, 0, count);
fileOutputStream.flush();
fileOutputStream.close();
zipInputStream.closeEntry();
}
}
zipInputStream.close();
// post-stuff
}
Try this:
ZipInputStream zis = null;
try {
zis = new ZipInputStream(new FileInputStream(zipFilePath));
ZipEntry entry;
while ((entry = zis.getNextEntry()) != null) {
// Create a file on HDD in the destinationPath directory
// destinationPath is a "root" folder, where you want to extract your ZIP file
File entryFile = new File(destinationPath, entry.getName());
if (entry.isDirectory()) {
if (entryFile.exists()) {
logger.log(Level.WARNING, "Directory {0} already exists!", entryFile);
} else {
entryFile.mkdirs();
}
} else {
// Make sure all folders exists (they should, but the safer, the better ;-))
if (entryFile.getParentFile() != null && !entryFile.getParentFile().exists()) {
entryFile.getParentFile().mkdirs();
}
// Create file on disk...
if (!entryFile.exists()) {
entryFile.createNewFile();
}
// and rewrite data from stream
OutputStream os = null;
try {
os = new FileOutputStream(entryFile);
IOUtils.copy(zis, os);
} finally {
IOUtils.closeQuietly(os);
}
}
}
} finally {
IOUtils.closeQuietly(zis);
}
Note, that it uses Apache Commons IO to handle stream copying / closing.
I have a war file which does not contains manifest not even META-INF folder. Now my problem is that I wrote a code which was working fine with normal war files containing manifests. Now I am required to read a war file which does not contain manifest.
When I check
while ((ze = zis.getNextEntry()) != null)
This condition is just skipped. Is there any API which treats it just as a normal zip file or is there any workaround.
I have tried with JarEntry as well as ZipEntry. Here is a small snippet that should be explanatory.
try {
FileInputStream fis = new FileInputStream(applicationPack);
ZipArchiveInputStream zis = new ZipArchiveInputStream(fis);
ArchiveEntry ze = null;
File applicationPackConfiguration;
while ((ze = zis.getNextEntry()) != null) {
// do someting
}
What can be done ?
You can simply list contents with ZipFile class:
try {
// Open the ZIP file
ZipFile zf = new ZipFile("filename.zip");
// Enumerate each entry
for (Enumeration entries = zf.entries(); entries.hasMoreElements();) {
// Get the entry name
String zipEntryName = ((ZipEntry)entries.nextElement()).getName();
}
} catch (IOException e) {
}
Example taken from here. Another example for retrieving the file from zip.
Update:
Code above indeed has problems with zip files that contain only directory as a top-level element.
This code works (tested):
try {
// Open the ZIP file
FileInputStream fis = new FileInputStream(new File("/your.war"));
ZipInputStream zis = new ZipInputStream(new BufferedInputStream(fis));
ZipEntry entry = null;
while ((entry = zis.getNextEntry()) != null)
// Get the entry name
System.out.println(entry.getName());
} catch (IOException e) {
}
You can use classes from java.util.zip package. Just replace ZipArchiveInputStream with ZipInputStream and ArchiveEntry with ZipEntry:
FileInputStream fis = new FileInputStream(new File("/path/to/your.war"));
ZipInputStream zis = new ZipInputStream(fis);
ZipEntry ze = null;
while ((ze = zis.getNextEntry()) != null) {
System.out.println(ze.getName());
}