Unable to extract folders from zip file using ZipEntry in windows - java

java.util.zip.ZipInputStream zis = new java.util.zip.ZipInputStream(new BufferedInputStream(is));
java.util.zip.ZipEntry entry;
new File(outdir+ File.separator+"changelog").delete();
new File(outdir+ File.separator+"media").delete();
try {
while ((entry = zis.getNextEntry()) != null) {
File of = new File(outdir + File.separator + entry.getName());
if (entry.isDirectory()) {
of.mkdirs();
continue;
} else {
File xx = new File(of.getParent());
if (!xx.exists()) {
Stack<String> todo = new Stack<String>();
do {
todo.push(xx.getAbsolutePath());
xx = new File(xx.getParent());
} while (!xx.exists());
while (todo.size() > 0) {
xx = new File(todo.pop());
if (!xx.exists()) {
xx.mkdirs();
}
}
}
}
BufferedOutputStream bos = new BufferedOutputStream(new FileOutputStream(of), buffer.length);
cpio(new BufferedInputStream(zis), bos, "unzip:" + entry.getName());
bos.flush();
bos.close();
}
} catch (IllegalArgumentException e) {
// problem with chars in entry name likely
}catch(Exception e){
System.out.println(e+"Srikanth");
}
zis.close();
}
entry.isDirectory() always returing false so it is creating files instead of directories. What is the problem?

ZipEntry from ZipInputStream representing empty directory at end of file with \ , directory with elements with /
So that entry.isDirectory() not working with empty directory.
Where as ZipEntry from ZipFile working fine. I think there is difference between ZipInputStream and ZipEntry behavior.

isDirectory is not working at all with files zipped with Windows standard option "sent to/ zip files"
The format of the zip is different than format generated with tools like 7zip or Winzip. (Nice to have a standard archive compression :D)

Related

Why does my program skip files when unzipping by java.util.zip?

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());
}
}

Java get the top level folder from zip file

I kind of stuck in this problem. I want to print only the top level directory from a zip file. For example I have a zip file with following structure:
Sample.zip
- sound
- game
-start.wav
-end.wav
- Intro
- custom
- Scene
- fight
- Angle
..............
Above figure shows: the Sample.zip has 2 folders (sound and custom), and inside sound there are 2 folders game and Intro and so on...
Now I know how to open and grab the directory from zip file: For example (working code)
try {
appFile = ("../../Sample.zip"); // just the path to zip file
ZipFile zipFile = new ZipFile(appFile);
Enumeration<? extends ZipEntry> entries = zipFile.entries();
while (entries.hasMoreElements()) {
ZipEntry entry = entries.nextElement();
if(entry.isDirectory()){
String dir = entry.getName();
File file = new File(dir);
System.out.println(file.getParent());
}
}
} catch (IOException e) {
System.out.println("Error opening Zip" +e);
}
Now I also know I can use .getParent()(as you see above) to get the top level dir, but the above implementation has not worked. It'll list out all the directory , like
null
sound
game
null
custom
scene
Angle
My question is how can I actually print only the top level folders, In above scenario , sound and custom ?
For any sort of hint, I'll be thankful.
Actually I did following as suggested by #JB Nizet and get a work around(it actually work ):
try {
appFile = ("../../Sample.zip"); // just the path to zip file
ZipFile zipFile = new ZipFile(appFile);
Enumeration<? extends ZipEntry> entries = zipFile.entries();
while (entries.hasMoreElements()) {
ZipEntry entry = entries.nextElement();
if(entry.isDirectory()){
File file = new File(entry.getName());
if(file.getParent() == null){
System.out.println(file.getName());
}
}
}
} catch (IOException e) {
System.out.println("Error opening Zip" +e);
}
The above solution has worked because the top level dir has no parent and therefore returned null as output. So I just loop around the directories to see if they have parents, if they dont have any parent then they are top level directory.
You can use something like that:
try{
String appFile = "../file.zip"; // just the path to zip file
ZipFile zipFile = new ZipFile(appFile);
Enumeration<? extends ZipEntry> entries = zipFile.entries();
while (entries.hasMoreElements()) {
ZipEntry entry = entries.nextElement();
if(entry.isDirectory() && !entry.getName().matches("\\S+/\\S+")){ //it's a top level folder
System.out.println(entry.getName());
}
}
} catch (IOException e) {
System.out.println("Error opening Zip" +e);
}
Maybe this code will help you with using InputStream
String topFolder="";
String topFolder2="";
Boolean hasTopFolder=true;
try{
File dir = new File(path+"/html5"+catalogue.getIdProduit());
if (!dir.exists()) {
dir.mkdirs();
}
String outputFolder= "path/to/outputFolder";
InputStream input = file.getInputstream();
//get the zip file content
ZipInputStream zis = new ZipInputStream(input);
//get the zipped file list entry
ZipEntry ze = zis.getNextEntry();
while(ze!=null){
if (ze.isDirectory()) {
System.out.println("is directory : "+ ze.getName());
if ("".equals(topFolder)){
topFolder = ze.getName().split("/")[0];
System.out.println("is directory topFolder : "+ ze.getName());
}
if (("".equals(topFolder2)) && (!topFolder.equals(ze.getName().split("/")[0]))){
hasTopFolder=false;
topFolder2=ze.getName().split("/")[0];
System.out.println("is directory topFolder2 : "+ ze.getName());
}
ze = zis.getNextEntry();
continue;
}
String fileName = ze.getName();
File newFile = new File(outputFolder + File.separator + fileName);
System.out.println("file unzip : "+ newFile.getAbsoluteFile());
//create all non exists folders
//else you will hit FileNotFoundException for compressed folder
new File(newFile.getParent()).mkdirs();
FileOutputStream fos = new FileOutputStream(newFile);
int len;
while ((len = zis.read(buffer)) > 0) {
fos.write(buffer, 0, len);
}
fos.close();
ze = zis.getNextEntry();
}
zis.closeEntry();
zis.close();
System.out.println("Done");
}
catch(IOException e){
e.printStackTrace();
}
if (hasTopFolder){
topFolder="/"+topFolder;
}
else
topFolder="";
What about following method:
/**
* Get the root folders within a zip file
*
* #param zipFile the zip file to be used. E.g. '/home/foo/bar.zip'
* #return a list containing all root folders
* #throws Exception if case the zip file cannot be found or read.
*/
public static List<String> getGetRootDirectoriesWithinZip(String zipFile) throws Exception {
Set<String> set = new LinkedHashSet();
//get the zip file content stream
ZipInputStream zipInputStream = new ZipInputStream(new FileInputStream(zipFile));
//get the zipped file set entry
ZipEntry zipEntry = zipInputStream.getNextEntry();
while (zipEntry != null) {
String fileName = zipEntry.getName();
Path path = Paths.get(fileName);
int nameCount = path.getNameCount();
for (int i = 0; i < nameCount; i++) {
if (path != null && path.getParent() != null) {
path = path.getParent();
}
}
set.add(path.toString());
zipEntry = zipInputStream.getNextEntry();
}
List<String> retList = new ArrayList<>();
retList.addAll(set);
return retList;
}
This is the method that worked for me.
I should note that I am using StringUtils (Apache Lang3) to count how many times
"\" appears in the ZipEntry path, although if you don't want to use StringUtils you could make
your own method for counting.
public static ArrayList<ZipEntry> getZipContent(File file, int index) {
try {
ArrayList<String> innerFoldersPaths = new ArrayList<String>();
ArrayList<ZipEntry> retEntries = new ArrayList<ZipEntry>();
ZipFile zipFile = new ZipFile(file);
Enumeration<? extends ZipEntry> entries = zipFile.entries();
while (entries.hasMoreElements()) {
ZipEntry entry = entries.nextElement();
// If you also want to get files remove the "if (entry.isDirectory())" statement.
if (entry.isDirectory()) {
String backSlashName = entry.getName().replace("/", "\\"); // Important to do this.
if (StringUtils.countMatches(backSlashName, "\\") > index - 1) { // Using Apache StringUtils
String folder[] = backSlashName.split(Pattern.quote("\\"));
String finalFolder = "";
// Getting the folders path inside the .zip file .
for (int i = 0; i < index; i++) {
folder[i] = folder[i] + "\\";
finalFolder = finalFolder + folder[i];
}
finalFolder = finalFolder.replace("\\", "/"); // Important to do this.
if (innerFoldersPaths.contains(finalFolder)) {
} else {
innerFoldersPaths.add(finalFolder);
}
}
}
}
for (String backSlashName : innerFoldersPaths) {
retEntries.add(zipFile.getEntry(backSlashName));
}
zipFile.close();
return retEntries;
} catch (Exception exception) {
// handle the exception in the way you want.
exception.printStackTrace();
}
return null;
}
The usage of this method:
File file = new File("Your zip file here");
for (ZipEntry zipEntry : getZipContent(file, 1)) { // This would return all the folders in the first file
// Do what ever your wantt with the ZipEntry
System.out.println(zipEntry.getName());
}
If you want to get all the folders past the first one,
you could do it by changing the index to how deep the folders that you want to get are.
use this fun in Kotlin
fun getRootFolderName(fileAddress: String): String {
if (File(fileAddress).parent == null || ("" + File(fileAddress).parent).length < 1) return File(fileAddress).name
return getRootFolderName("" + File(fileAddress).parent)
}

what does getNextEntry() do?

I have checked java api document, it says getNextEntry() Reads the next ZIP file entry and positions the stream at the beginning of the entry data.
what does it mean "reads the NEXT zip file"? why the "NEXT" ?
I have this piece of code, what's the point of this line ze = zin.getNextEntry()?
public void unzip() {
try {
FileInputStream fin = new FileInputStream(_zipFile);
ZipInputStream zin = new ZipInputStream(fin);
ZipEntry ze = null;
while ((ze = zin.getNextEntry()) != null) {
Log.v("Decompress", "Unzipping " + ze.getName());
if(ze.isDirectory()) {
_dirChecker(ze.getName());
} else {
FileOutputStream fout = new FileOutputStream(_location + ze.getName());
for (int c = zin.read(); c != -1; c = zin.read()) {
fout.write(c);
}
zin.closeEntry();
fout.close();
}
}
zin.close();
} catch(Exception e) {
Log.e("Decompress", "unzip", e);
}
}
It reads the next entry within the zip file.
A zip file logically contains main other files - so foo.zip can contain files a.txt and b.txt. The getNextEntry() moves you on to the next file within the archive.
(I've never been particularly keen on the way that ZipInputStream is modeled using inheritance from InputStream, but that's a different matter.)

Extracting zip file into a folder throws "Invalid entry size (expected 46284 but got 46285 bytes)" for one of the entry

When I am trying to extract the zip file into a folder as per the below code, for one of the entry (A text File) getting an error as "Invalid entry size (expected 46284 but got 46285 bytes)" and my extraction is stopping abruptly. My zip file contains around 12 text files and 20 TIF files. It is encountering the problem for the text file and is not able to proceed further as it is coming into the Catch block.
I face this problem only in Production Server which is running on Unix and there is no problem with the other servers(Dev, Test, UAT).
We are getting the zip into the servers path through an external team who does the file transfer and then my code starts working to extract the zip file.
...
int BUFFER = 2048;
java.io.BufferedOutputStream dest = null;
String ZipExtractDir = "/y34/ToBeProcessed/";
java.io.File MyDirectory = new java.io.File(ZipExtractDir);
MyDirectory.mkdir();
ZipFilePath = "/y34/work_ZipResults/Test.zip";
// Creating fileinputstream for zip file
java.io.FileInputStream fis = new java.io.FileInputStream(ZipFilePath);
// Creating zipinputstream for using fileinputstream
java.util.zip.ZipInputStream zis = new java.util.zip.ZipInputStream(new java.io.BufferedInputStream(fis));
java.util.zip.ZipEntry entry;
while ((entry = zis.getNextEntry()) != null)
{
int count;
byte data[] = new byte[BUFFER];
java.io.File f = new java.io.File(ZipExtractDir + "/" + entry.getName());
// write the files to the directory created above
java.io.FileOutputStream fos = new java.io.FileOutputStream(ZipExtractDir + "/" + entry.getName());
dest = new java.io.BufferedOutputStream(fos, BUFFER);
while ((count = zis.read(data, 0, BUFFER)) != -1)
{
dest.write(data, 0, count);
}
dest.flush();
dest.close();
}
zis.close();
zis.closeEntry();
}
catch (Exception Ex)
{
System.Out.Println("Exception in \"ExtractZIPFiles\"---- " + Ex.getMessage());
}
I can't understand the problem you're meeting, but here is the method I use to unzip an archive:
public static void unzip(File zip, File extractTo) throws IOException {
ZipFile archive = new ZipFile(zip);
Enumeration<? extends ZipEntry> e = archive.entries();
while (e.hasMoreElements()) {
ZipEntry entry = e.nextElement();
File file = new File(extractTo, entry.getName());
if (entry.isDirectory()) {
file.mkdirs();
} else {
if (!file.getParentFile().exists()) {
file.getParentFile().mkdirs();
}
InputStream in = archive.getInputStream(entry);
BufferedOutputStream out = new BufferedOutputStream(new FileOutputStream(file));
IOUtils.copy(in, out);
in.close();
out.close();
}
}
}
Calling:
File zip = new File("/path/to/my/file.zip");
File extractTo = new File("/path/to/my/destination/folder");
unzip(zip, extractTo);
I never met any issue with the code above, so I hope that could help you.
Off the top of my head, I could think of these reasons:
There could be problem with the encoding of the text file.
The file needs to be read/transferred in "binary" mode.
There could be an issue with the line ending \n or \r\n
The file could simply be corrupt. Try opening the file with a zip utility.

How does one go about finding a specific directory out of a jar/zip file in java?

I have been working on this for quite a few hours. I can't seem to find the issue to this problem. Essentially what I have is this:
I have a jar, let's call it "a.jar"
I need to get the directory "z" and it's contents from "a.jar", but "z" isn't in the root directory of "a.jar".
"z" is in "/x/y/" and "/x/y/" is in "a.jar", so it looks like this:
"a.jar/x/y/z/"
I hope that's a decent explanation. By the way, "a.jar" is what everything is running out of, so its in the class path obviously.
Basically for each ZipEntry you have to check if it isDirectory() and parse that also.
Checkout this link:
http://www.javaworld.com/javaworld/javatips/jw-javatip49.html
LE:
Here is a complete example that extracts the files from the jar, and if you specify a specific path it will extract only that folder:
public void doUnzip(String inputZip, String destinationDirectory, String specificPath)
throws IOException {
int BUFFER = 2048;
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();
if(specificPath != null){
if(entry.getName().startsWith(specificPath) == false)
continue;
}
File destFile = new File(unzipDestinationDirectory, entry.getName());
// create the parent directory structure if needed
destFile.getParentFile().mkdirs();
try {
// extract file if not a directory
if (!entry.isDirectory()) {
BufferedInputStream is = new BufferedInputStream(
zipFile.getInputStream(entry));
// 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
for (int bytesRead; (bytesRead = is.read(data, 0, BUFFER)) != -1;) {
dest.write(data, 0, bytesRead);
}
dest.flush();
dest.close();
is.close();
}
} catch (IOException ioe) {
ioe.printStackTrace();
}
}
zipFile.close();
}
public static void main(String[] args) {
Unzip unzip = new Unzip();
try {
unzip.doUnzip("test.jar", "output", "x/y/z");
} catch (IOException e) {
e.printStackTrace();
}
}
..(ZipEntry), but they don't work very well with sub-directories.
They work just fine. Iterate the entries and simply check the path equates to that sub-directory. If it does, add it to a list (or process it, whatever).

Categories

Resources