A proprietary program that I'm working with zips up and extracts certain files without changing the modified date of the files when unzipping. I'm also creating my own zip and extraction tool based off the source code in our program but when I'm unzipping the files the modified date of all zipped files is showing with the unzip time & date. Here's the code for my extraction:
public static int unzipFiles(File zipFile, File extractDir) throws Exception
{
int totalFileCount = 0;
String zipFilePath = zipFile.getPath();
System.out.println("Zip File Path: " + zipFilePath);
ZipFile zfile = new ZipFile(zipFile);
System.out.println("Size of ZipFile: "+zfile.size());
Enumeration<? extends ZipEntry> entries = zfile.entries();
while (entries.hasMoreElements())
{
ZipEntry entry = entries.nextElement();
System.out.println("ZipEntry File: " + entry.getName());
File file = new File(extractDir, entry.getName());
if (entry.isDirectory())
{
System.out.println("Creating Directory");
file.mkdirs();
}
else
{
file.getParentFile().mkdirs();
InputStream in = zfile.getInputStream(entry);
try
{
copy(in, file);
}
finally
{
in.close();
}
}
totalFileCount++;
}
return totalFileCount;
}
private static void copy(InputStream in, OutputStream out) throws IOException
{
byte[] buffer = new byte[1024];
System.out.println("InputStream/OutputStram copy");
while (true)
{
int readCount = in.read(buffer);
if (readCount < 0)
{
break;
}
out.write(buffer, 0, readCount);
}
}
I'm sure there is a better way to do this other than doing the inputstream/outputstream copy. I'm sure this is the culprit as doing an extraction with winRAR does not change the date with the files I zipped.
Use ZipEntry.getTime to get the last-modified time and File.setLastModified to set it on the file after you are done copying it.
Related
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.
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.
Is there an easy way to recursively ZIP a directory that may or may not contain any number of files and any number of levels of subdirectories?
public final class ZipFileUtil {
public static void zipDirectory(File dir, File zipFile) throws IOException {
FileOutputStream fout = new FileOutputStream(zipFile);
ZipOutputStream zout = new ZipOutputStream(fout);
zipSubDirectory("", dir, zout);
zout.close();
}
private static void zipSubDirectory(String basePath, File dir, ZipOutputStream zout) throws IOException {
byte[] buffer = new byte[4096];
File[] files = dir.listFiles();
for (File file : files) {
if (file.isDirectory()) {
String path = basePath + file.getName() + "/";
zout.putNextEntry(new ZipEntry(path));
zipSubDirectory(path, file, zout);
zout.closeEntry();
} else {
FileInputStream fin = new FileInputStream(file);
zout.putNextEntry(new ZipEntry(basePath + file.getName()));
int length;
while ((length = fin.read(buffer)) > 0) {
zout.write(buffer, 0, length);
}
zout.closeEntry();
fin.close();
}
}
}
}
You can use the Java API Specification
and How do you recursively traverse through file folders?.
I use the ZipFileSystem implementation in ruby with great success, though I've never used it in java. You might want to check this out:
I'm having a hard time trying to tar some files using the compress library.
My code is the following, and is taken from the commons.compress wiki exemples :
private static File createTarFile(String[] filePaths, String saveAs) throws Exception{
File tarFile = new File(saveAs);
OutputStream out = new FileOutputStream(tarFile);
TarArchiveOutputStream aos = (TarArchiveOutputStream) new ArchiveStreamFactory().createArchiveOutputStream("tar", out);
for(String filePath : filePaths){
File file = new File(filePath);
TarArchiveEntry entry = new TarArchiveEntry(file);
entry.setSize(file.length());
aos.putArchiveEntry(entry);
IOUtils.copy(new FileInputStream(file), aos);
aos.closeArchiveEntry();
}
aos.finish();
out.close();
return tarFile;
}
There is no error during the process, but when I try to untar the file, I got the following :
XXXX:XXXX /home/XXXX$ tar -xf typeCommandes.tar
tar: Unexpected EOF in archive
tar: Unexpected EOF in archive
tar: Error is not recoverable: exiting now
Also, the archive IS slighty smaller in size than the original file, which isnt normal for a tar, so there DO is a problem...
-rw-r--r-- 1 XXXX nobody 12902400 Jan 14 17:11 typeCommandes.tar
-rw-r--r-- 1 XXXX nobody 12901888 Jan 14 17:16 typeCommandes.csv
Anyone can tell me what I'm doing wrong ? Thanks
You're not closing the TarArchiveOutputStream. Add aos.close() after aos.finish()
Small correction to the code above.
It does not close input stream, while Apache lib assumes that stream is managed by calling client.
See the fix below (put this code after the line 'aos.putArchiveEntry(entry)') :
FileInputStream fis = new FileInputStream(fileForPuttingIntoTar);
IOUtils.copy(fis, aos);
fis.close();
aos.closeArchiveEntry();
the example here -> http://commons.apache.org/compress/examples.html uses the method putNextEntry(entry) which you seem to omit.
See also my answer here
import org.apache.commons.compress.archivers.ArchiveEntry;
import org.apache.commons.compress.archivers.tar.TarArchiveEntry;
import org.apache.commons.compress.archivers.tar.TarArchiveInputStream;
import org.apache.commons.compress.archivers.tar.TarArchiveOutputStream;
public class TarUpdater {
private static final int buffersize = 8048;
public static void updateFile(File tarFile, File[] flist) throws IOException {
// get a temp file
File tempFile = File.createTempFile(tarFile.getName(), null);
// delete it, otherwise you cannot rename your existing tar to it.
if (tempFile.exists()) {
tempFile.delete();
}
if (!tarFile.exists()) {
tarFile.createNewFile();
}
boolean renameOk = tarFile.renameTo(tempFile);
if (!renameOk) {
throw new RuntimeException(
"could not rename the file " + tarFile.getAbsolutePath() + " to " + tempFile.getAbsolutePath());
}
byte[] buf = new byte[buffersize];
TarArchiveInputStream tin = new TarArchiveInputStream(new FileInputStream(tempFile));
OutputStream outputStream = new BufferedOutputStream(Files.newOutputStream(tarFile.toPath()));
TarArchiveOutputStream tos = new TarArchiveOutputStream(outputStream);
tos.setLongFileMode(TarArchiveOutputStream.LONGFILE_POSIX);
//read from previous version of tar file
ArchiveEntry entry = tin.getNextEntry();
while (entry != null) {//previous file have entries
String name = entry.getName();
boolean notInFiles = true;
for (File f : flist) {
if (f.getName().equals(name)) {
notInFiles = false;
break;
}
}
if (notInFiles) {
// Add TAR entry to output stream.
if (!entry.isDirectory()) {
tos.putArchiveEntry(new TarArchiveEntry(name));
// Transfer bytes from the TAR file to the output file
int len;
while ((len = tin.read(buf)) > 0) {
tos.write(buf, 0, len);
}
}
}
entry = tin.getNextEntry();
}
// Close the streams
tin.close();//finished reading existing entries
// Compress new files
for (int i = 0; i < flist.length; i++) {
if (flist[i].isDirectory()) {
continue;
}
InputStream fis = new FileInputStream(flist[i]);
TarArchiveEntry te = new TarArchiveEntry(flist[i],flist[i].getName());
//te.setSize(flist[i].length());
tos.setLongFileMode(TarArchiveOutputStream.LONGFILE_GNU);
tos.setBigNumberMode(2);
tos.putArchiveEntry(te); // Add TAR entry to output stream.
// Transfer bytes from the file to the TAR file
int count = 0;
while ((count = fis.read(buf, 0, buffersize)) != -1) {
tos.write(buf, 0, count);
}
tos.closeArchiveEntry();
fis.close();
}
// Complete the TAR file
tos.close();
tempFile.delete();
}
}
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.