I have zip file which contains some other zip files.
For example, the mail file is abc.zip and it contains xyz.zip, class1.java, class2.java. And xyz.zip contains the file class3.java and class4.java.
So I need to extract the zip file using Java to a folder that should contain class1.java, class2.java, class3.java and class4.java.
Warning, the code here is ok for trusted zip files, there's no path validation before write which may lead to security vulnerability as described in zip-slip-vulnerability if you use it to deflate an uploaded zip file from unknown client.
This solution is very similar to the previous solutions already posted, but this one recreates the proper folder structure on unzip.
public static void extractFolder(String zipFile) throws IOException {
int buffer = 2048;
File file = new File(zipFile);
try (ZipFile zip = new ZipFile(file)) {
String newPath = zipFile.substring(0, zipFile.length() - 4);
new File(newPath).mkdir();
Enumeration<? extends ZipEntry> zipFileEntries = zip.entries();
// Process each entry
while (zipFileEntries.hasMoreElements()) {
// grab a zip file entry
ZipEntry entry = zipFileEntries.nextElement();
String currentEntry = entry.getName();
File destFile = new File(newPath, currentEntry);
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);
try (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();
is.close();
}
}
if (currentEntry.endsWith(".zip")) {
// found a zip file, try to open
extractFolder(destFile.getAbsolutePath());
}
}
}
}
Here's some untested code base on some old code I had that unzipped files.
public void doUnzip(String inputZip, String destinationDirectory)
throws IOException {
int BUFFER = 2048;
List zipFiles = new ArrayList();
File sourceZipFile = new File(inputZip);
File unzipDestinationDirectory = new File(destinationDirectory);
unzipDestinationDirectory.mkdir();
ZipFile zipFile;
// Open Zip file for reading
zipFile = new ZipFile(sourceZipFile, ZipFile.OPEN_READ);
// Create an enumeration of the entries in the zip file
Enumeration zipFileEntries = zipFile.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(unzipDestinationDirectory, currentEntry);
destFile = new File(unzipDestinationDirectory, destFile.getName());
if (currentEntry.endsWith(".zip")) {
zipFiles.add(destFile.getAbsolutePath());
}
// grab file's parent directory structure
File destinationParent = destFile.getParentFile();
// create the parent directory structure if needed
destinationParent.mkdirs();
try {
// extract file if not a directory
if (!entry.isDirectory()) {
BufferedInputStream is =
new BufferedInputStream(zipFile.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();
}
} catch (IOException ioe) {
ioe.printStackTrace();
}
}
zipFile.close();
for (Iterator iter = zipFiles.iterator(); iter.hasNext();) {
String zipName = (String)iter.next();
doUnzip(
zipName,
destinationDirectory +
File.separatorChar +
zipName.substring(0,zipName.lastIndexOf(".zip"))
);
}
}
I take ca.anderson4 and remove the List zipFiles and rewrite a little bit, this is what i got:
public class Unzip {
public void unzip(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
unzip(destFile.getAbsolutePath());
}
}
}
public static void main(String[] args) {
Unzip unzipper=new Unzip();
try {
unzipper.unzip("test/test.zip");
} catch (ZipException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
I tested and it works
In testing I noticed File.mkDirs() does not work under Windows...
/**
* for a given full path name recreate all parent directories
**/
private void createParentHierarchy(String parentName) throws IOException {
File parent = new File(parentName);
String[] parentsStrArr = parent.getAbsolutePath().split(File.separator == "/" ? "/" : "\\\\");
//create the parents of the parent
for(int i=0; i < parentsStrArr.length; i++){
StringBuffer currParentPath = new StringBuffer();
for(int j = 0; j < i; j++){
currParentPath.append(parentsStrArr[j]+File.separator);
}
File currParent = new File(currParentPath.toString());
if(!currParent.isDirectory()){
boolean created = currParent.mkdir();
if(isVerbose)log("creating directory "+currParent.getAbsolutePath());
}
}
//create the parent itself
if(!parent.isDirectory()){
boolean success = parent.mkdir();
}
}
Modified as i needed then mixed in a bit of the best answers. This version will:
Recursively Extract a zip to given location
Create empty directories
Close zip properly
public static void unZipAll(File source, File destination) throws IOException
{
System.out.println("Unzipping - " + source.getName());
int BUFFER = 2048;
ZipFile zip = new ZipFile(source);
try{
destination.getParentFile().mkdirs();
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(destination, 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 = null;
FileOutputStream fos = null;
BufferedOutputStream dest = null;
try{
is = new BufferedInputStream(zip.getInputStream(entry));
int currentByte;
// establish buffer for writing file
byte data[] = new byte[BUFFER];
// write the current file to disk
fos = new FileOutputStream(destFile);
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);
}
} catch (Exception e){
System.out.println("unable to extract entry:" + entry.getName());
throw e;
} finally{
if (dest != null){
dest.close();
}
if (fos != null){
fos.close();
}
if (is != null){
is.close();
}
}
}else{
//Create directory
destFile.mkdirs();
}
if (currentEntry.endsWith(".zip"))
{
// found a zip file, try to extract
unZipAll(destFile, destinationParent);
if(!destFile.delete()){
System.out.println("Could not delete zip");
}
}
}
} catch(Exception e){
e.printStackTrace();
System.out.println("Failed to successfully unzip:" + source.getName());
} finally {
zip.close();
}
System.out.println("Done Unzipping:" + source.getName());
}
One should CLOSE zip file after unzip.
static 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);
try
{
...code from other answers ( ex. NeilMonday )...
}
finally
{
zip.close();
}
}
Same as NeilMonday's answer, but extracts empty directories:
static 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();
}
else{
destFile.mkdirs()
}
if (currentEntry.endsWith(".zip"))
{
// found a zip file, try to open
extractFolder(destFile.getAbsolutePath());
}
}
}
Here is some code, which I tested to be working quite well :
package com.test;
import java.io.BufferedInputStream;
import java.io.BufferedOutputStream;
import java.io.File;
import java.io.FileOutputStream;
import java.util.Enumeration;
import java.util.zip.ZipEntry;
import java.util.zip.ZipFile;
public class Unzipper {
private final static int BUFFER_SIZE = 2048;
private final static String ZIP_FILE = "/home/anton/test/test.zip";
private final static String DESTINATION_DIRECTORY = "/home/anton/test/";
private final static String ZIP_EXTENSION = ".zip";
public static void main(String[] args) {
System.out.println("Trying to unzip file " + ZIP_FILE);
Unzipper unzip = new Unzipper();
if (unzip.unzipToFile(ZIP_FILE, DESTINATION_DIRECTORY)) {
System.out.println("Succefully unzipped to the directory "
+ DESTINATION_DIRECTORY);
} else {
System.out.println("There was some error during extracting archive to the directory "
+ DESTINATION_DIRECTORY);
}
}
public boolean unzipToFile(String srcZipFileName,
String destDirectoryName) {
try {
BufferedInputStream bufIS = null;
// create the destination directory structure (if needed)
File destDirectory = new File(destDirectoryName);
destDirectory.mkdirs();
// open archive for reading
File file = new File(srcZipFileName);
ZipFile zipFile = new ZipFile(file, ZipFile.OPEN_READ);
//for every zip archive entry do
Enumeration<? extends ZipEntry> zipFileEntries = zipFile.entries();
while (zipFileEntries.hasMoreElements()) {
ZipEntry entry = (ZipEntry) zipFileEntries.nextElement();
System.out.println("\tExtracting entry: " + entry);
//create destination file
File destFile = new File(destDirectory, entry.getName());
//create parent directories if needed
File parentDestFile = destFile.getParentFile();
parentDestFile.mkdirs();
if (!entry.isDirectory()) {
bufIS = new BufferedInputStream(
zipFile.getInputStream(entry));
int currentByte;
// buffer for writing file
byte data[] = new byte[BUFFER_SIZE];
// write the current file to disk
FileOutputStream fOS = new FileOutputStream(destFile);
BufferedOutputStream bufOS = new BufferedOutputStream(fOS, BUFFER_SIZE);
while ((currentByte = bufIS.read(data, 0, BUFFER_SIZE)) != -1) {
bufOS.write(data, 0, currentByte);
}
// close BufferedOutputStream
bufOS.flush();
bufOS.close();
// recursively unzip files
if (entry.getName().toLowerCase().endsWith(ZIP_EXTENSION)) {
String zipFilePath = destDirectory.getPath() + File.separatorChar + entry.getName();
unzipToFile(zipFilePath, zipFilePath.substring(0,
zipFilePath.length() - ZIP_EXTENSION.length()));
}
}
}
bufIS.close();
return true;
} catch (Exception e) {
e.printStackTrace();
return false;
}
}
}
I tried with the top voted answer here, and that does not recursively unzip the files, it just unzips the files of the first level.
Source : Solution which extracts files into a given directory
Also, check this solution by the same person : Solution which extracts file in memory
No third-party dependencies, guards against zip slip, fully commented, recreates directory structure recursively, ignores empty directories, sane source code nesting, extracts to zip file's directory, and uses UTF-8. Usage:
Path zipFile = Path.of( "/path/to/filename.zip" );
Zip.extract( zipFile );
Here's the code:
import java.io.IOException;
import java.nio.charset.StandardCharsets;
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.zip.ZipEntry;
import java.util.zip.ZipFile;
import static java.nio.file.Files.createDirectories;
import static java.nio.file.StandardCopyOption.REPLACE_EXISTING;
/**
* Responsible for managing zipped archive files.
*/
public final class Zip {
/**
* Extracts the contents of the zip archive into its current directory. The
* contents of the archive must be {#link StandardCharsets#UTF_8}. For
* example, if the {#link Path} is <code>/tmp/filename.zip</code>, then
* the contents of the file will be extracted into <code>/tmp</code>.
*
* #param zipPath The {#link Path} to the zip file to extract.
* #throws IOException Could not extract the zip file, zip entries, or find
* the parent directory that contains the path to the
* zip archive.
*/
public static void extract( final Path zipPath ) throws IOException {
assert !zipPath.toFile().isDirectory();
try( final var zipFile = new ZipFile( zipPath.toFile() ) ) {
iterate( zipFile );
}
}
/**
* Extracts each entry in the zip archive file.
*
* #param zipFile The archive to extract.
* #throws IOException Could not extract the zip file entry.
*/
private static void iterate( final ZipFile zipFile )
throws IOException {
// Determine the directory name where the zip archive resides. Files will
// be extracted relative to that directory.
final var path = getDirectory( zipFile );
final var entries = zipFile.entries();
while( entries.hasMoreElements() ) {
final var zipEntry = entries.nextElement();
final var zipEntryPath = path.resolve( zipEntry.getName() );
// Guard against zip slip.
if( zipEntryPath.normalize().startsWith( path ) ) {
extract( zipFile, zipEntry, zipEntryPath );
}
}
}
/**
* Extracts a single entry of a zip file to a given directory. This will
* create the necessary directory path if it doesn't exist. Empty
* directories are not re-created.
*
* #param zipFile The zip archive to extract.
* #param zipEntry An entry in the zip archive.
* #param zipEntryPath The file location to write the zip entry.
* #throws IOException Could not extract the zip file entry.
*/
private static void extract(
final ZipFile zipFile,
final ZipEntry zipEntry,
final Path zipEntryPath ) throws IOException {
// Only attempt to extract files, skipping empty directories.
if( !zipEntry.isDirectory() ) {
createDirectories( zipEntryPath.getParent() );
try( final var in = zipFile.getInputStream( zipEntry ) ) {
Files.copy( in, zipEntryPath, REPLACE_EXISTING );
}
}
}
/**
* Helper method to return the normalized directory where the given archive
* resides.
*
* #param zipFile The {#link ZipFile} having a path to normalize.
* #return The directory containing the given {#link ZipFile}.
* #throws IOException The zip file has no parent directory.
*/
private static Path getDirectory( final ZipFile zipFile ) throws IOException {
final var zipPath = Path.of( zipFile.getName() );
final var parent = zipPath.getParent();
if( parent == null ) {
throw new IOException( zipFile.getName() + " has no parent directory." );
}
return parent.normalize();
}
}
Now that you have the core algorithm in place, you need to check the file extension for ".zip" and, if present, recursively call Zip.extract( ... ) on that file.
File dir = new File("BASE DIRECTORY PATH");
FileFilter ff = new FileFilter() {
#Override
public boolean accept(File f) {
//only want zip files
return (f.isFile() && f.getName().toLowerCase().endsWith(".zip"));
}
};
File[] list = null;
while ((list = dir.listFiles(ff)).length > 0) {
File file1 = list[0];
//TODO unzip the file to the base directory
}
Related
I read quite a few articles, but I did not find a similar problem and its solution.
I'm try to read all files and some skipped with method zis.getNextEntry
public static void main(String[] args) throws Exception {
String fileZip = "src/main/resources/unzipTest/fias_xml.zip";
ZipInputStream zis = new ZipInputStream(new FileInputStream(fileZip));
ZipEntry entry;
while ((entry = zis.getNextEntry()) != null) {
System.out.println(entry.getName());
}
}
}
But if you unzip with WinRar, for example, everything will be unzipped correctly
Archive files
After running the program
Or how i can see why some files doesn't read?
Can the archive be broken?
After I unzipped and re-zipped the files by using winrar, the program worked correctly. Why was winrar able to do this, but the java code was not?
zipArchive
jdk1.8.0_161
Based on the test i did i able to print each directory and file name correctly.
There 2 scenario came to my mind:
i) the filename length or the complete length is more what the platform can handle. But this also should be same case while do unzip from winrar
ii) Was there any permission issue, but again it won't be selective manner.
can you please let me which jdk version ?
Will u be able to sent me the zip file, I can try to simulate.
public void unzip(String zipFile, String destDir)
{
try
{
int BUFFER = 8*1024;
File file = new File(zipFile);
ZipFile zip = new ZipFile(file);
String newPath = destDir;
new File(newPath).mkdir();
Enumeration zipFileEntries = zip.entries();
while (zipFileEntries.hasMoreElements())
{
ZipEntry entry = (ZipEntry) zipFileEntries.nextElement();
String currentEntry = entry.getName();
File destFile = new File(newPath, currentEntry);
File destinationParent = destFile.getParentFile();
destinationParent.mkdirs();
if (!entry.isDirectory())
{
BufferedInputStream is = new BufferedInputStream(zip
.getInputStream(entry));
int currentByte;
byte[] data = new byte[BUFFER];
FileOutputStream fos = new FileOutputStream(destFile);
BufferedOutputStream dest = new BufferedOutputStream(fos,
BUFFER);
while ((currentByte = is.read(data, 0, BUFFER)) != -1) {
dest.write(data, 0, currentByte);
}
dest.flush();
dest.close();
is.close();
}
}
}
catch (Exception e)
{
System.out.println(e.getMessage());
}
}
I am trying to delete a zip file after unziping. but I am not able to delete it:
if (file.getName().contains(".zip")) {
System.out.println(file.getAbsolutePath()); // I am getting the correct path
file.delete();
System.out.println(file.getName()); // I am getting the correct name Script-1.zip
}
This is the full code
public class Zip4 {
public static void main(String[] args) {
File[] files = new File(args[0]).listFiles();
for(File file : files)
// System.out.println(file.getName());
//if(file.getName().contains("1400") && file.getName().contains(".zip"))
extractFolder(args[0] + file.getName(), args[1]);
DeleteFiles();
// for(File file : files)
// System.out.println("File:C:/1/"+ file.getName());
// extractFolder(args[0]+file.getName(),args[1]);
}
private static void DeleteFiles()
{
File f = null;
File[] paths;
f = new File("D:/Copyof");
paths = f.listFiles();
for(File path:paths)
{
// prints file and directory paths
if(path.getName().contains("J14_0_0RC") || path.getName().contains(".zip") || path.getName().contains(".log"))
{
//System.out.println(path);
path.delete();
}
}
}
private static void extractFolder(String zipFile,String extractFolder)
{
try
{
int BUFFER = 2048;
File file = new File(zipFile);
ZipFile zip = new ZipFile(file);
String newPath = extractFolder;
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();
fos.flush();
fos.close();
is.close();
}
}
if(file.getName().contains(".zip"))
{
System.out.println(file.getAbsolutePath());
file.delete();
System.out.println(file.getName());
}
}
catch (Exception e)
{
System.out.println("Error: " + e.getMessage());
}
}
}
ZipFile is a closeable resource. So either close() it once you're done in a finally block or create it with try-with-resources (since java7):
try(ZipFile zip = new ZipFile(file)){
//unzip here
}
file.delete();
Apart from this, you should revisit this block
dest.flush();
dest.close();
fos.flush();
fos.close();
is.close();
which is quite prone to resource leaks. If one of the upper calls fails, all subsequent calls are not invoked, resulting in unclosed resources and resource leakage.
So best would be to use try-with-resources here, too.
try(BufferedInputStream is = new BufferedInputStream(zip.getInputStream(entry));
FileOutputStream fos = new FileOutputStream(destFile);
BufferedOutputStream dest = new BufferedOutputStream(fos, BUFFER)) {
//write the data
} //all streams are closed implicitly here
Or use an existing tool for that, for example Apache Commons IO IOUtil.closeQuietly(resource) or embedd every single call into
if(resource != null) {
try{
resource.close();
} catch(IOException e){
//omit
}
}
You could also omit the call to flush() which is done implicitly when closing the resource.
given a zip file with multiple nested directory structure, how do I unzip it into the same tree structure?
does ZipFile.entries() provide the enumeration in any order?
This is mine.
In file you specify the file you want to expand
in target dir you have to specify the target location as "new File("/tmp/foo/bar")". If you want to extract in the current directory you can specify targetDir = new File(".")
public static void unzip(File file, File targetDir) throws ZipException,
IOException {
targetDir.mkdirs();
ZipFile zipFile = new ZipFile(file);
try {
Enumeration<? extends ZipEntry> entries = zipFile.entries();
while (entries.hasMoreElements()) {
ZipEntry entry = entries.nextElement();
File targetFile = new File(targetDir, entry.getName());
if (entry.isDirectory()) {
targetFile.mkdirs();
} else {
InputStream input = zipFile.getInputStream(entry);
try {
OutputStream output = new FileOutputStream(targetFile);
try {
copy(input, output);
} finally {
output.close();
}
} finally {
input.close();
}
}
}
} finally {
zipFile.close();
}
}
private static void copy(InputStream input, OutputStream output)
throws IOException {
byte[] buffer = new byte[4096];
int size;
while ((size = input.read(buffer)) != -1)
output.write(buffer, 0, size);
}
Worked for me. Good luck.
Here's the one I use all the times. It should directly work after a copy/paste and in any circumstances.
public static File unzip(File inFile, File outFolder)
{ final int BUFFER = 2048;
try
{
BufferedOutputStream out = null;
ZipInputStream in = new ZipInputStream(
new BufferedInputStream(
new FileInputStream(inFile)));
ZipEntry entry;
while((entry = in.getNextEntry()) != null)
{
//System.out.println("Extracting: " + entry);
int count;
byte data[] = new byte[BUFFER];
//We will try to reconstruct the entry directories
File entrySupposedPath = new File(outFolder.getAbsolutePath()+File.separator+entry.getName());
//Does the parent folder exist?
if (!entrySupposedPath.getParentFile().exists()){
entrySupposedPath.getParentFile().mkdirs();
}
// write the files to the disk
out = new BufferedOutputStream(
new FileOutputStream(outFolder.getPath() + "/" + entry.getName()),BUFFER);
while ((count = in.read(data,0,BUFFER)) != -1)
{
out.write(data,0,count);
}
out.flush();
out.close();
}
in.close();
return outFolder;
}
catch(Exception e)
{
e.printStackTrace();
return inFile;
}
}
Zip doesn't offer directory structure per se. The tree alike structure is built by having full path of each entry. ZipFile enumerates the entries in the same way they have been added to the file.
Note: java.util.ZipEntry.isDirectory() just tests if the last character of the name is '/', that's how it works.
What you need to extract the files into the same directory. Parse then name like that:
for(ZipEntry zipEntry : java.util.Collections.list(zipFile.entries())){//lazislav
String name = zipEntry.getName();
int idx = name.lastIndexOf('/');
if (idx>=0) name=name.substring(idx)
if (name.length()==0) continue;
File f = new File(targetDir, name);
}
That shall do it more or less (you still need to take care of duplicate file names, etc)
ZipFile zipFile = new ZipFile("archive.zip");
try {
for (Enumeration<? extends ZipEntry> entries = zipFile.entries(); entries.hasMoreElements();) {
ZipEntry entry = entries.nextElement();
if (entry.isDirectory()) {
new File(entry.getName()).mkdirs();
} else {
InputStream in = zipFile.getInputStream(entry);
try {
OutputStream out = new BufferedOutputStream(new FileOutputStream(entry.getName()));
try {
// this util class is taken from apache commons io (see http://commons.apache.org/io/)
IOUtils.copy(in, out);
} finally {
out.close();
}
} finally {
in.close();
}
}
}
} catch (IOException e) {
e.printStackTrace();
} finally {
zipFile.close();
}
Why do you care about order?
If the ZipFile entry has a name /a/b/c/file.txt, then you can work out the directory name /a/b/c and then create a directory in your tree called a/b/c.
I am using the following code to extract a zip file in Java.
import java.io.*;
import java.util.zip.*;
class testZipFiles
{
public static void main(String[] args)
{
try
{
String filename = "C:\\zip\\includes.zip";
testZipFiles list = new testZipFiles( );
list.getZipFiles(filename);
}
catch (Exception e)
{
e.printStackTrace();
}
}
public void getZipFiles(String filename)
{
try
{
String destinationname = "c:\\zip\\";
byte[] buf = new byte[1024];
ZipInputStream zipinputstream = null;
ZipEntry zipentry;
zipinputstream = new ZipInputStream(
new FileInputStream(filename));
zipentry = zipinputstream.getNextEntry();
while (zipentry != null)
{
//for each entry to be extracted
String entryName = zipentry.getName();
System.out.println("entryname "+entryName);
int n;
FileOutputStream fileoutputstream;
File newFile = new File(entryName);
String directory = newFile.getParent();
if(directory == null)
{
if(newFile.isDirectory())
break;
}
fileoutputstream = new FileOutputStream(
destinationname+entryName);
while ((n = zipinputstream.read(buf, 0, 1024)) > -1)
fileoutputstream.write(buf, 0, n);
fileoutputstream.close();
zipinputstream.closeEntry();
zipentry = zipinputstream.getNextEntry();
}//while
zipinputstream.close();
}
catch (Exception e)
{
e.printStackTrace();
}
}
}
Obviously this will not extract a folder tree because of the break statement. I tried to use recursion to process a folder tree but failed. Could someone show me how to improve this code to handle a folder tree instead of a compressed single level folder.
You can use File.mkdirs() to create folders. Try changing your method like this:
public static void getZipFiles(String filename) {
try {
String destinationname = "c:\\zip\\";
byte[] buf = new byte[1024];
ZipInputStream zipinputstream = null;
ZipEntry zipentry;
zipinputstream = new ZipInputStream(
new FileInputStream(filename));
zipentry = zipinputstream.getNextEntry();
while (zipentry != null) {
//for each entry to be extracted
String entryName = destinationname + zipentry.getName();
entryName = entryName.replace('/', File.separatorChar);
entryName = entryName.replace('\\', File.separatorChar);
System.out.println("entryname " + entryName);
int n;
FileOutputStream fileoutputstream;
File newFile = new File(entryName);
if (zipentry.isDirectory()) {
if (!newFile.mkdirs()) {
break;
}
zipentry = zipinputstream.getNextEntry();
continue;
}
fileoutputstream = new FileOutputStream(entryName);
while ((n = zipinputstream.read(buf, 0, 1024)) > -1) {
fileoutputstream.write(buf, 0, n);
}
fileoutputstream.close();
zipinputstream.closeEntry();
zipentry = zipinputstream.getNextEntry();
}//while
zipinputstream.close();
} catch (Exception e) {
e.printStackTrace();
}
}
Another option is commons-compress, for which there is sample code on the site linked above.
I needed to do this because of an API which I was using required a File parameter, which you can't get from a resource in a JAR.
I found that the answer from #Emre didn't work correctly. For some reason ZipEntry skipped a few files in the JAR (no apparent pattern to this). I fixed this by using JarEntry instead. There is also a bug in the above code where the file in the zip entry could be enumerated before the directory is, which causes an exception because the directory hasn't been created yet.
Note that the below code depends on Apache Commons utility classes.
/**
*
* Extract a directory in a JAR on the classpath to an output folder.
*
* Note: User's responsibility to ensure that the files are actually in a JAR.
* The way that I do this is to get the URI with
* URI url = getClass().getResource("/myresource").toURI();
* and then if url.isOpaque() we are in a JAR. There may be a more reliable
* way however, please edit this answer if you know of one.
*
* #param classInJar A class in the JAR file which is on the classpath
* #param resourceDirectory Path to resource directory in JAR
* #param outputDirectory Directory to write to
* #return String containing the path to the folder in the outputDirectory
* #throws IOException
*/
private static String extractDirectoryFromClasspathJAR(Class<?> classInJar, String resourceDirectory, String outputDirectory)
throws IOException {
resourceDirectory = StringUtils.strip(resourceDirectory, "\\/") + File.separator;
URL jar = classInJar.getProtectionDomain().getCodeSource().getLocation();
//Note: If you want to extract from a named JAR, remove the above
//line and replace "jar.getFile()" below with the path to the JAR.
JarFile jarFile = new JarFile(new File(jar.getFile()));
byte[] buf = new byte[1024];
Enumeration<JarEntry> jarEntries = jarFile.entries();
while (jarEntries.hasMoreElements()) {
JarEntry jarEntry = jarEntries.nextElement();
if (jarEntry.isDirectory() || !jarEntry.getName().startsWith(resourceDirectory)) {
continue;
}
String outputFileName = FilenameUtils.concat(outputDirectory, jarEntry.getName());
//Create directories if they don't exist
new File(FilenameUtils.getFullPath(outputFileName)).mkdirs();
//Write file
FileOutputStream fileOutputStream = new FileOutputStream(outputFileName);
int n;
InputStream is = jarFile.getInputStream(jarEntry);
while ((n = is.read(buf, 0, 1024)) > -1) {
fileOutputStream.write(buf, 0, n);
}
is.close();
fileOutputStream.close();
}
jarFile.close();
String fullPath = FilenameUtils.concat(outputDirectory, resourceDirectory);
return fullPath;
}
I get the following IOException :
java.io.IOException: Access is denied
at java.io.WinNTFileSystem.createFileExclusively(Native Method)
at java.io.File.createNewFile(File.java:850)
at zipUnzipper.main(zipUnzipper.java:41)
When trying to run the following piece of code :
public class zipUnzipper {
public zipUnzipper() {
}
public static void main(String[] args){
//Unzip to temp folder. Add all files to mFiles. Print names of all files in mFfiles.
File file = new File("C:\\aZipFile.zip");
String filename = file.getName();
String filePathName = new String();
int o = filename.lastIndexOf('.');
filename = filename.substring(0,o);
try {
ZipFile zipFile = new ZipFile (file.getAbsoluteFile());
Enumeration entries = zipFile.entries();
while(entries.hasMoreElements()) {
ZipEntry zipEntry = (ZipEntry) entries.nextElement();
System.out.println("Unzipping: " + zipEntry.getName());
BufferedInputStream bis = new BufferedInputStream(zipFile.getInputStream(zipEntry));
byte[] buffer = new byte[2048];
filePathName = "C:\\TEMP\\"+filename+"\\";
File fileToWrite = new File(filePathName+ zipEntry.getName());
fileToWrite.mkdirs();
fileToWrite.createNewFile();
FileOutputStream fos = new FileOutputStream(fileToWrite);
BufferedOutputStream bos = new BufferedOutputStream( fos , buffer.length );
int size;
while ((size = bis.read(buffer, 0, buffer.length)) != -1) {
bos.write(buffer, 0, size);
}
bos.flush();
bos.close();
bis.close();
}
zipFile.close();
File folder = new File (filePathName);
File [] mFiles = folder.listFiles();
for (int x=0; x<mFiles.length; x++) {
System.out.println(mFiles[x].getAbsolutePath());
}
} catch (ZipException ze) {
ze.printStackTrace();
} catch (IOException ioe) {
ioe.printStackTrace();
}
}
}
It seems to me that for some reason the JVM can't create a new file. The code runs perfectly well if the files already exist. Is there some kind of access file which dictates whether the JVM can create a new file or am I simply doing something wrong?
Any help is much appreciated :-)
I'm running Java 1.4 and have been testing in JDeveloper in Windows XP.
The issue is that these calls step on each other:
fileToWrite.mkdirs(); //creates a directory e.g. C:\temp\foo\x
fileToWrite.createNewFile(); //attempts to create a file C:\temp\foo\x
The create operation fails because you just created a directory with the same name than the file you want to create.
What you want to do instead is:
fileToWrite.getParentFile().mkdirs()
And also, the call to createNewFile() is unnecessary.
Based on your code. The following "unzips" a zip file:
import java.io.*;
import java.util.zip.ZipFile;
import java.util.zip.ZipEntry;
import java.util.Enumeration;
public class Unzipper {
public static void main(String[] args)
throws IOException {
final File file = new File(args[0]);
final ZipFile zipFile = new ZipFile(file);
final byte[] buffer = new byte[2048];
final File tmpDir = new File(System.getProperty("java.io.tmpdir"), zipFile.getName());
if(!tmpDir.mkdir() && tmpDir.exists()) {
System.err.println("Cannot create: " + tmpDir);
System.exit(0);
}
for(final Enumeration entries = zipFile.entries(); entries.hasMoreElements();) {
final ZipEntry zipEntry = (ZipEntry) entries.nextElement();
System.out.println("Unzipping: " + zipEntry.getName());
final InputStream is = zipFile.getInputStream(zipEntry);
final File fileToWrite = new File(tmpDir, zipEntry.getName());
final File folder = fileToWrite.getParentFile();
if(!folder.mkdirs() && !folder.exists()) {
System.err.println("Cannot create: " + folder);
System.exit(0);
}
if(!zipEntry.isDirectory()) {
//No need to use buffered streams since we're doing our own buffering
final FileOutputStream fos = new FileOutputStream(fileToWrite);
int size;
while ((size = is.read(buffer)) != -1) {
fos.write(buffer, 0, size);
}
fos.close();
is.close();
}
}
zipFile.close();
}
}
Disclaimer: I haven't tested it beyond the very basics.
Why are you calling createNewFile()? Just create the FileOutputStream.
It also could be that in context where you are launching the application you haven't access rights to the place where you are trying to create the file. Launch the app as admin or create the file in the project folder.