ZipEntry is not writing file into directory - java

I am trying to write multiple files into a directory and a zip file. that directory and zip file are supposed to be contained in another zip. so my desired zip structure would look like this:
parent.zip
- testDirectory/
- testZip.zip
When I read my files into a byte[] and write them into my directory testDirectory nothing is written into the directory. the directory is empty when I open my zip.
On the other hand when I write my byte[] array into a zip file called testZip.zip the files will be correctly written into the zip file.
parent.zip, testDirectory/ and testZip.zip are all being created. the only problem is that testDirectory/ is empty.
What is the solution to my problem?
private void addFiles(Document document, ZipOutputStream zos) throw FileNotFoundException, IOException {
byte[] mediaFilesDirectory = getMediaFiles();
ZipEntry entry = new ZipEntry(document.getId() + File.separator + "testDirectory/");
entry.setSize(mediaFilesDirectory.length);
zos.putNextEntry(entry);
zos.write(mediaFilesDirectory);
zos.closeEntry();
ZipEntry entry2 = new ZipEntry(document.getId() + File.separator + "testZip.zip");
entry2.setSize(mediaFilesDirectory.length);
zos.putNextEntry(entry2);
zos.write(mediaFilesDirectory);
zos.closeEntry();
}
private byte[] getMediaFiles() throws FileNotFoundException {
try (ByteArrayOutputStream baos = new ByteArrayOutputStream(); ZipOutputStream zos = new ZipOutputStream(baos)) {
byte[] bytes = "Hello World".getBytes();
String entryName = "test.txt";
ZipEntry entry = new ZipEntry(entryName);
entry.setSize(bytes.length);
zos.putNextEntry(entry);
zos.write(bytes);
zos.closeEntry();
zos.close();
return baos.toByteArray();
} catch (Exception exc) {
throw new FileNotFoundException("Exception");
}
}
No Exceptions are being thrown. If I comment out the ZipEntry for testDirectory/, then testZip.zip will still correctly be created. If I comment out testZip.zip, then testDirectory/ will still be empty.
Here some test code to run locally. it suffers from the same problem as my code:
import java.io.*;
import java.util.zip.ZipEntry;
import java.util.zip.ZipOutputStream;
public class Main {
public static void main(String[] args) {
System.out.println("Make sure to change the file variable");
File file = new File("C:\\Users\\YourUser\\test.zip"); //TODO enter a valid path here
try(ZipOutputStream zos = new ZipOutputStream(new FileOutputStream(file))) {
addFiles(zos);
zos.closeEntry();
} catch (Exception e) {
System.out.println(e);
}
}
private static void addFiles(ZipOutputStream zos) throws Exception {
byte[] mediaFilesDirectory = getMediaFiles();
ZipEntry entry = new ZipEntry("1" + File.separator + "testDirectory/");
entry.setSize(mediaFilesDirectory.length);
zos.putNextEntry(entry);
zos.write(mediaFilesDirectory);
zos.closeEntry();
ZipEntry entry2 = new ZipEntry("1" + File.separator + "testZip.zip");
entry2.setSize(mediaFilesDirectory.length);
zos.putNextEntry(entry2);
zos.write(mediaFilesDirectory);
zos.closeEntry();
}
private static byte[] getMediaFiles() throws Exception {
try (ByteArrayOutputStream baos = new ByteArrayOutputStream(); ZipOutputStream zos = new ZipOutputStream(baos)) {
byte[] bytes = "Hello World".getBytes();
String entryName = "test.txt";
ZipEntry entry = new ZipEntry(entryName);
entry.setSize(bytes.length);
zos.putNextEntry(entry);
zos.write(bytes);
zos.closeEntry();
zos.close();
return baos.toByteArray();
}
}
}

ZipEntry entry = new ZipEntry(document.getId() + File.separator + "testDirectory/");
entry.setSize(mediaFilesDirectory.length);
zos.putNextEntry(entry);
zos.write(mediaFilesDirectory);
zos.closeEntry();
This code makes no sense.
It creates a directory, and directories do not themselves contain anything. In a zip file, a directory is just a string, a marker to the unzip tool: Make this dir.
A zip file is JUST a mapping of strings representing filenames to data representing the contents, that is it, zip files are not hierarchical.
If you want to write 'foo.txt' into the zip file as well as 'subdir/bar.txt', you would do:
ZipEntry entry = new ZipEntry("foo.txt");
zos.putNextEntry(entry);
zos.write(dataOfFooTxt);
ZipEntry entry = new ZipEntry("subdir/");
zos.putNextEntry(entry);
ZipEntry entry = new ZipEntry("subdir/bar.txt");
zos.putNextEntry(entry);
zos.write(dataOfBarTxt);
You just put an entry and do nothing further. Note also that zips always use forward slash for pathing, File.separator is something you should never use in java, except for one sole purpose: Creating strings fit to show to users. That's all. forward slash is the path separator for java apps. Even if they run on windows.
What you are attempting to do (make a zip which you the nwant to put in a zip) would result in the command:
unzip myzip.zip
To unpack 2 files: 'test.txt' and 'innerzip.zip', which is, itself a zip file. This can be done but is really weird, and certainly doesn't compress things any better. I'm not sure if that is your intent or if you're just confused and you think this is how you put subdirs in zip files. If it's the latter - just do what the second snippet in this answer does - do not make zips-in-zips.

Related

zipping files in java memory produces a zip file but the files inside are empty

I am trying to zip multiple files in Java to be used in my jar. 2 files are images and 1 is an HTML temp file. Upon zipping these files, when I try to see the contents of the zip file, all 3 files have become empty. There is no error being thrown as the files are in the zip but they are empty for some reason. I need to save my zip file in memory.
Here is my zip code.
public static File zipPdf(File data, File cover) throws IOException {
ArrayList<ByteArrayOutputStream> zips = new ArrayList<>();
ClassLoader loader = RunningPDF.class.getClassLoader();
File image = new File(Objects.requireNonNull(loader.getResource("chekklogo.png")).getFile());
File man = new File(Objects.requireNonNull(loader.getResource("manlogo.jpg")).getFile());
ByteArrayOutputStream baos = new ByteArrayOutputStream();
try(ZipOutputStream zos = new ZipOutputStream(baos)) {
ZipEntry entry = new ZipEntry(data.getName());
zos.putNextEntry(entry);
ZipEntry entry2 = new ZipEntry(image.getName());
zos.putNextEntry(entry2);
ZipEntry entry3 = new ZipEntry(man.getName());
zos.putNextEntry(entry3);
} catch(IOException ioe) {
ioe.printStackTrace();
}
You forget to write the bytes. The putNextEntry just adds an entry. You need to explicitly write the bytes. Go with the following
File file = new File(filePath);
String zipFileName = file.getName().concat(".zip");
FileOutputStream fos = new FileOutputStream(zipFileName);
ZipOutputStream zos = new ZipOutputStream(fos);
zos.putNextEntry(new ZipEntry(file.getName()));
byte[] bytes = Files.readAllBytes(Paths.get(filePath));
zos.write(bytes, 0, bytes.length);
zos.closeEntry();
zos.close();

Cannot delete zip file after unzipping?

I am facing an unusal issue.I am building a tool which is scheduled to run every 5 mins.
It will pick up the zip files from a particular directory and extract files (depending on the file name) to a destination. I am using zipentry to get each filename in zip file and then extracting as required then I back them (zip files, once I finish all the files in a zip) to a particular directory and then delete the zip file. But sometimes (not always) the zip files do not get deleted. Since I am using fileutils.forcedelete(). I am getting an exception: unable to delete file. So I changed to the code to using fileutils.forcedeleteonexit() still some files remain in the source.
Here is a sample of my code:
sourceFile=new file(zipfile);
zipFile = new ZipFile(sourceFile);
zEnum = (Enumeration<ZipEntry>) zipFile.entries();
for (int a = 0; a < zipFile.size(); a++)
{
ZipEntry zE = zEnum.nextElement();
//Function uses zip4j for extracting. No streams used.
extract(String sourceZipFile, String fileNameToExtract, String outputFolder);
}
//I tried it with finally either
zipFile.close();
//Using fileutils to copy. No streams used.
copyFile(sourceFile, backup);
FileUtils.forceDeleteOnExit(sourceFile);
There are no streams used but I am getting a lock on files sometimes (not always).
What seems to be the bug here? Is it the zip4j extraction that is causing the problem or anything else? I am using zip4j 1.3.1.
I think your problem related with OS file buffers, that sometimes are not flushed when you are trying to delete file.
Did you try to use sourceFile.deleteOnExit() instead FileUtils.forceDeleteOnExit(sourceFile)?
Also you can try to check sourceFile.canWrite before deleting (may be it may helps)
You can also try to use FileInputStream() before deleting:
FileInputStream fi = new FileInputStream(sourceFile);
fi.getFD().sync();
Use apache-commons IO's FileDeleteStrategy. Something like:
FileDeleteStrategy.FORCE.delete(file);
Update:
It should be the way IO is being handled in your application. I have written simple code which copies a zip file to a temporary zip, deflates the temporary zip and after few seconds deletes it. Here you go:
public class ZipTest {
private static String dirPath = "/home/ubuntuuser/Desktop/";
public static void main(String[] args) throws Exception {
File myzip = new File(dirPath + "content.zip");
String tempFileStr = dirPath + "content_temp.zip";
File tempFile = new File(tempFileStr);
String unzipFolderStr = dirPath + "unzip";
copyUsingChannels(myzip, tempFile);
// copyUsingStreams(myzip, tempFile);
unZip(tempFileStr, unzipFolderStr);
Thread.sleep(3000);
tempFile.delete();
}
private static void copyUsingStreams(File myzip, File tempFile)
throws IOException, FileNotFoundException {
byte[] barray = new byte[1024];
if (!tempFile.exists())
{
tempFile.createNewFile();
}
FileOutputStream fos = new FileOutputStream(tempFile);
FileInputStream fis = new FileInputStream(myzip);
int length = 0;
while ((length = fis.read(barray)) != -1)
{
fos.write(barray, 0, length);
}
fis.close();
fos.close();
}
public static void copyUsingChannels(final File srcFile, final File destFile) throws Exception
{
if (!destFile.exists())
{
destFile.createNewFile();
}
FileChannel source = new FileInputStream(srcFile).getChannel();
FileChannel destination = new FileOutputStream(destFile).getChannel();
source.transferTo(0, source.size(), destination);
source.close();
destination.close();
}
private static void unZip(String zipFile, String outputFolder) throws Exception {
byte[] buffer = new byte[1024];
File folder = new File(outputFolder);
if (!folder.exists()) {
folder.mkdir();
}
ZipInputStream zis = new ZipInputStream(new FileInputStream(zipFile));
ZipEntry ze = zis.getNextEntry();
while (ze != null) {
String fileName = ze.getName();
File newFile = new File(outputFolder + File.separator + fileName);
System.out.println("file unzip : " + newFile.getAbsoluteFile());
new File(newFile.getParent()).mkdirs();
if (ze.isDirectory())
{
newFile.mkdir();
ze = zis.getNextEntry();
continue;
}
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();
}
}

How to change file extension at runtime in Java

I am trying to implement program to zip and unzip a file. All I want to do is to zip a file (fileName.fileExtension) with name as fileName.zip and on unzipping change it again to fileName.fileExtension.
This is how I used to rename files or change its extension.
public static void modify(File file)
{
int index = file.getName().lastIndexOf(".");
//print filename
//System.out.println(file.getName().substring(0, index));
//print extension
//System.out.println(file.getName().substring(index));
String ext = file.getName().substring(index);
//use file.renameTo() to rename the file
file.renameTo(new File("Newname"+ext));
}
edit: John's method renames the file (keeping the extension). To change the extension do:
public static File changeExtension(File f, String newExtension) {
int i = f.getName().lastIndexOf('.');
String name = f.getName().substring(0,i);
return new File(f.getParent(), name + newExtension);
}
This changes only the last extension to a filename, i.e. the .gz part of archive.tar.gz. Therefore it works fine with Linux hidden files, for which the name starts with a .
This is quite safe because if getParent() returns null (i.e. in the event of the parent being the system root) it is "cast" to an empty String as the whole argument to the File constructor is evaluated first.
The only case where you will get a funny output is if you pass in a File representing the system root itself, in which case the null is prepended to the rest of the path string.
Try with:
File file = new File("fileName.zip"); // handler to your ZIP file
File file2 = new File("fileName.fileExtension"); // destination dir of your file
boolean success = file.renameTo(file2);
if (success) {
// File has been renamed
}
I would check, if the file has an extension before changing. The solution below works also with files without extension or multiple extensions
public File changeExtension(File file, String extension) {
String filename = file.getName();
if (filename.contains(".")) {
filename = filename.substring(0, filename.lastIndexOf('.'));
}
filename += "." + extension;
file.renameTo(new File(file.getParentFile(), filename));
return file;
}
#Test
public void test() {
assertThat(changeExtension(new File("C:/a/aaa.bbb.ccc"), "txt"),
is(new File("C:/a/aaa.bbb.txt")));
assertThat(changeExtension(new File("C:/a/test"), "txt"),
is(new File("C:/a/test.txt")));
}
By the same logic as mentioned #hsz, but instead simply use replacement:
File file = new File("fileName.fileExtension"); // creating object of File
String str = file.getPath().replace(".fileExtension", ".zip"); // replacing extension to another
file.renameTo(new File(str));
I want to avoid the new extension just happening to be in the path or filename itself. I like a combination of java.nio and apache StringFilenameUtils.
public void changeExtension(Path file, String extension) throws IOException {
String newFilename = FilenameUtils.removeExtension(file.toString()) + EXTENSION_SEPARATOR_STR + extension;
Files.move(file, Paths.get(newFilename, StandardCopyOption.REPLACE_EXISTING));
}
If you are using Kotlin you can use from this property of your file object:
file.nameWithoutExtension + "extension"
FilenameUtils.getFullPathNoEndSeparator(doc.getDocLoc()) + "/" +
FilenameUtils.getBaseName(doc.getDocLoc()) + ".xml"
My firend was working on a zipper in Java some 4 months back, I got this code from him.
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import java.util.zip.ZipEntry;
import java.util.zip.ZipOutputStream;
public class ZipFiles {
List<String> filesListInDir = new ArrayList<String>();
public static void main(String[] args) {
File file = new File("/Users/pankaj/sitemap.xml");
String zipFileName = "/Users/pankaj/sitemap.zip";
File dir = new File("/Users/pankaj/tmp");
String zipDirName = "/Users/pankaj/tmp.zip";
zipSingleFile(file, zipFileName);
ZipFiles zipFiles = new ZipFiles();
zipFiles.zipDirectory(dir, zipDirName);
}
/**
* This method zips the directory
* #param dir
* #param zipDirName
*/
private void zipDirectory(File dir, String zipDirName) {
try {
populateFilesList(dir);
//now zip files one by one
//create ZipOutputStream to write to the zip file
FileOutputStream fos = new FileOutputStream(zipDirName);
ZipOutputStream zos = new ZipOutputStream(fos);
for(String filePath : filesListInDir){
System.out.println("Zipping "+filePath);
//for ZipEntry we need to keep only relative file path, so we used substring on absolute path
ZipEntry ze = new ZipEntry(filePath.substring(dir.getAbsolutePath().length()+1, filePath.length()));
zos.putNextEntry(ze);
//read the file and write to ZipOutputStream
FileInputStream fis = new FileInputStream(filePath);
byte[] buffer = new byte[1024];
int len;
while ((len = fis.read(buffer)) > 0) {
zos.write(buffer, 0, len);
}
zos.closeEntry();
fis.close();
}
zos.close();
fos.close();
} catch (IOException e) {
e.printStackTrace();
}
}
/**
* This method populates all the files in a directory to a List
* #param dir
* #throws IOException
*/
private void populateFilesList(File dir) throws IOException {
File[] files = dir.listFiles();
for(File file : files){
if(file.isFile())
filesListInDir.add(file.getAbsolutePath());
else
populateFilesList(file);
}
}
/**
* This method compresses the single file to zip format
* #param file
* #param zipFileName
*/
private static void zipSingleFile(File file, String zipFileName) {
try {
//create ZipOutputStream to write to the zip file
FileOutputStream fos = new FileOutputStream(zipFileName);
ZipOutputStream zos = new ZipOutputStream(fos);
//add a new Zip Entry to the ZipOutputStream
ZipEntry ze = new ZipEntry(file.getName());
zos.putNextEntry(ze);
//read the file and write to ZipOutputStream
FileInputStream fis = new FileInputStream(file);
byte[] buffer = new byte[1024];
int len;
while ((len = fis.read(buffer)) > 0) {
zos.write(buffer, 0, len);
}
//Close the zip entry to write to zip file
zos.closeEntry();
//Close resources
zos.close();
fis.close();
fos.close();
System.out.println(file.getCanonicalPath()+" is zipped to "+zipFileName);
} catch (IOException e) {
e.printStackTrace();
}
}
}
I haven't tried it personally, but he and also some of my other friends told me that it works.

FileNotFoundException when trying to unzip an archive with java.util.zip.ZipFile

I have a silly problem i haven't been able to figure out. Can anyone help me?
My Code is as:
String zipname = "C:/1100.zip";
String output = "C:/1100";
BufferedInputStream bis = null;
BufferedOutputStream bos = null;
try {
ZipFile zipFile = new ZipFile(zipname);
Enumeration<?> enumeration = zipFile.entries();
while (enumeration.hasMoreElements()) {
ZipEntry zipEntry = (ZipEntry) enumeration.nextElement();
System.out.println("Unzipping: " + zipEntry.getName());
bis = new BufferedInputStream(zipFile.getInputStream(zipEntry));
int size;
byte[] buffer = new byte[2048];
It doesn't create a folder but debugging shows all the contents being generated.
In Order to create a folder i used the code
if(!output.exists()){ output.mkdir();} // here i get an error saying filenotfoundexception
bos = new BufferedOutputStream(new FileOutputStream(new File(outPut)));
while ((size = bis.read(buffer)) != -1) {
bos.write(buffer, 0, size);
}
}
} catch (Exception ex) {
ex.printStackTrace();
} finally {
bos.flush();
bos.close();
bis.close();
}
My zip file contains images: a.jpg b.jpg... and in the same hierarchy, I have abc.xml.
I need to extract the content as is in the zip file.
Any helps here.
There are a few problems with your code: Where is outPut declared? output is not a file but a string, so exists() and mkdir() do not exist. Start by declaring output like:
File output = new File("C:/1100");
Furthermore, outPut (with big P) is not declared. It be something like output + File.seprator + zipEntry.getName().
bos = new BufferedOutputStream(new FileOutputStream(output + File.seprator + zipEntry.getName()));
Note that you don't need to pass a File to FileOutputStream, as constructors show in the documentation.
At this point, your code should work if your Zip file does not contain directory. However, when opening the output stream, if zipEntry.getName() has a directory component (for instance somedir/filename.txt), opening the stream will result in a FileNotFoundException, as the parent directory of the file you try to create does not exist. If you want to be able to handle such zip files, you will find your answer in: How to unzip files recursively in Java?

File Path appearing in the Zipped File

I have a java program as below for zipping a folder as a whole.
public static void zipDir(String dir2zip, ZipOutputStream zos)
{
try
{
File zipDir= new File(dir2zip);
String[] dirList = zipDir.list();
byte[] readBuffer = new byte[2156];
int bytesIn = 0;
for(int i=0; i<dirList.length; i++)
{
File f = new File(zipDir, dirList[i]);
if(f.isDirectory())
{
String filePath = f.getPath();
zipDir(filePath, zos);
continue;
}
FileInputStream fis = new FileInputStream(f);
ZipEntry anEntry = new ZipEntry(f.getPath());
zos.putNextEntry(anEntry);
while((bytesIn = fis.read(readBuffer)) != -1)
{
zos.write(readBuffer, 0, bytesIn);
}
fis.close();
}
}
catch(Exception e)
{
e.printStackTrace();
}
}
public static void main(){
String date=new java.text.SimpleDateFormat("MM-dd-yyyy").format(new java.util.Date());
ZipOutputStream zos = new ZipOutputStream(new FileOutputStream("Output/" + date + "_RB" + ".zip"));
zipDir("Output/" + date + "_RB", zos);
zos.close();
}
My query here is. The target folder(+date+_RB) to be zipped is present inside the folder named Output. After successful zipping, when I extract the zipped file, I find a folder Output inside which the (+date+_RB) required folder is present. I need not want that Output folder after the extraction of the zipped file, rather it should directly extract the required folder alone. Please advise on the same.
UPDATE:
I tried Isaac's answer. While extracting the resultant zip file, no folders are getting extracted. Only the files inside all the folders are getting extracted. I just dont need the folder "Output" alone in the resultant zip file. But what the program does is, it doesnt extracts all other folders inside the Output folder, rather it just extracts the files inside those folders. Kindly advise on how to proceed...
It happens because of this:
ZipEntry anEntry = new ZipEntry(f.getPath());
f.getPath() will return Output/ at the beginning of the string. This is due to the flow of your program and how it (mis)uses File objects.
I suggest you construct a File object called, say, tmp:
File tmp = new File(dirList[i]);
The change the construction of f:
File f = new File(zipDir, tmp.getPath());
Then, change this:
ZipEntry anEntry = new ZipEntry(f.getPath());
To this:
ZipEntry anEntry = new ZipEntry(tmp.getPath());
I didn't have time to actually test it, but in a nutshell, your problem is due to how the File object is constructed.

Categories