ZIP and TAR fomats (and the old AR format) allow file append without a full rewrite. However:
- The Java archive classes DO NOT support this mode of operation.
- File append is likely to result in multiple copies of a file in the archive if you append an existing file.
- The ZIP and AR formats have a directory that needs to be rewritten following a file append operation. The standard utilities take precautions when rewriting the directory, but it is possible in theory that you may end up with an archive with a missing or corrupted directory if the append fails.
The ZIP file format was designed to allow appends without a total re-write and is ubiquitous, even on Unix.
http://stackoverflow.com/questions/2993847/append-files-to-an-archive-without-reading-rewriting-the-whole-archive/2993964#2993964
I am using java.util.zip to add some configuration resources into a jar file. when I call addFileToZip() method it overwrites the jar completely, instead of adding the file to the jar. Why I need to write the config to the jar is completely irrelevant. and I do not wish to use any external API's.
EDIT: The jar is not running in the VM and org.cfg.resource is the package I'm trying to save the file to, the file is a standard text document and the jar being edited contains the proper information before this method is used.
My code:
public void addFileToZip(File fileToAdd, File zipFile) { ZipOutputStream zos = null; FileInputStream fis = null; ZipEntry ze = null; byte[] buffer = null; int len; try { zos = new ZipOutputStream(new FileOutputStream(zipFile)); } catch (FileNotFoundException e) { } ze = new ZipEntry("org" + File.separator + "cfg" + File.separator + "resource" + File.separator + fileToAdd.getName()); try { zos.putNextEntry(ze); fis = new FileInputStream(fileToAdd); buffer = new byte[(int) fileToAdd.length()]; while((len = fis.read(buffer)) > 0) { zos.write(buffer, 0, len); } } catch (IOException e) { } try { zos.flush(); zos.close(); fis.close(); } catch (IOException e) { } }
The code you showed overrides a file no matter if it would be a zip file or not. ZipOutputStream
does not care about existing data. Neither any stream oriented API does.
I would recommend
- Create new file using
ZipOutputStream
. - Open existing with
ZipInputStream
- Copy existing entries to new file.
- Add new entries.
- Replace old file with a new one.
Hopefully in Java 7 we got Zip File System that will save you a lot of work.
We can directly write to files inside zip files
Map<String, String> env = new HashMap<>(); env.put("create", "true"); Path path = Paths.get("test.zip"); URI uri = URI.create("jar:" + path.toUri()); try (FileSystem fs = FileSystems.newFileSystem(uri, env)) { Path nf = fs.getPath("new.txt"); try (Writer writer = Files.newBufferedWriter(nf, StandardCharsets.UTF_8, StandardOpenOption.CREATE)) { writer.write("hello"); } }
http://stackoverflow.com/questions/17500856/java-util-zip-adding-a-new-file-overwrites-entire-jar?lq=1
As others mentioned, it's not possible to append content to an existing zip (or war). However, it's possible to create a new zip on the fly without temporarily writing extracted content to disk. It's hard to guess how much faster this will be, but it's the fastest you can get (at least as far as I know) with standard Java. As mentioned by Carlos Tasada, SevenZipJBindings might squeeze out you some extra seconds, but porting this approach to SevenZipJBindings will still be faster than using temporary files with the same library.
Here's some code that writes the contents of an existing zip (war.zip) and appends an extra file (answer.txt) to a new zip (append.zip). All it takes is Java 5 or later, no extra libraries needed.
public static void addFilesToExistingZip(File zipFile, File[] files) throws IOException { // get a temp file File tempFile = File.createTempFile(zipFile.getName(), null); // delete it, otherwise you cannot rename your existing zip to it. tempFile.delete(); boolean renameOk=zipFile.renameTo(tempFile); if (!renameOk) { throw new RuntimeException("could not rename the file "+zipFile.getAbsolutePath()+" to "+tempFile.getAbsolutePath()); } byte[] buf = new byte[1024]; ZipInputStream zin = new ZipInputStream(new FileInputStream(tempFile)); ZipOutputStream out = new ZipOutputStream(new FileOutputStream(zipFile)); ZipEntry entry = zin.getNextEntry(); while (entry != null) { String name = entry.getName(); boolean notInFiles = true; for (File f : files) { if (f.getName().equals(name)) { notInFiles = false; break; } } if (notInFiles) { // Add ZIP entry to output stream. out.putNextEntry(new ZipEntry(name)); // Transfer bytes from the ZIP file to the output file int len; while ((len = zin.read(buf)) > 0) { out.write(buf, 0, len); } } entry = zin.getNextEntry(); } // Close the streams zin.close(); // Compress the files for (int i = 0; i < files.length; i++) { InputStream in = new FileInputStream(files[i]); // Add ZIP entry to output stream. out.putNextEntry(new ZipEntry(files[i].getName())); // Transfer bytes from the file to the ZIP file int len; while ((len = in.read(buf)) > 0) { out.write(buf, 0, len); } // Complete the entry out.closeEntry(); in.close(); } // Complete the ZIP file out.close(); tempFile.delete(); }
public static void addFilesToZip(File source, File[] files) { try { File tmpZip = File.createTempFile(source.getName(), null); tmpZip.delete(); if(!source.renameTo(tmpZip)) { throw new Exception("Could not make temp file (" + source.getName() + ")"); } byte[] buffer = new byte[1024]; ZipInputStream zin = new ZipInputStream(new FileInputStream(tmpZip)); ZipOutputStream out = new ZipOutputStream(new FileOutputStream(source)); for(int i = 0; i < files.length; i++) { InputStream in = new FileInputStream(files[i]); out.putNextEntry(new ZipEntry(files[i].getName())); for(int read = in.read(buffer); read > -1; read = in.read(buffer)) { out.write(buffer, 0, read); } out.closeEntry(); in.close(); } for(ZipEntry ze = zin.getNextEntry(); ze != null; ze = zin.getNextEntry()) { out.putNextEntry(ze); for(int read = zin.read(buffer); read > -1; read = zin.read(buffer)) { out.write(buffer, 0, read); } out.closeEntry(); } out.close(); tmpZip.delete(); } catch(Exception e) { e.printStackTrace(); } }
you cannot simply "append" data to a war file or zip file, but it is not because there is an "end of file" indication, strictly speaking, in a war file. It is because the war (zip) format includes a directory, which is normally present at the end of the file, that contains metadata for the various entries in the war file. Naively appending to a war file results in no update to the directory, and so you just have a war file with junk appended to it.
http://stackoverflow.com/questions/2223434/appending-files-to-a-zip-file-with-java
How can I add entries to an existing zip file in Java
You could use zipFile.entries() to get an enumeration of all of the ZipEntry objects in the existing file,
loop through them and add them all to the ZipOutputStream, and then add your new entries in addition.
The function renames the existing zip file to a temporary file and then adds all entries in the existing zip along with the new files, excluding the zip entries that have the same name as one of the new files.
public static void addFilesToExistingZip(File zipFile, File[] files) throws IOException { // get a temp file File tempFile = File.createTempFile(zipFile.getName(), null); // delete it, otherwise you cannot rename your existing zip to it. tempFile.delete(); boolean renameOk=zipFile.renameTo(tempFile); if (!renameOk) { throw new RuntimeException("could not rename the file "+zipFile.getAbsolutePath()+" to "+tempFile.getAbsolutePath()); } byte[] buf = new byte[1024]; ZipInputStream zin = new ZipInputStream(new FileInputStream(tempFile)); ZipOutputStream out = new ZipOutputStream(new FileOutputStream(zipFile)); ZipEntry entry = zin.getNextEntry(); while (entry != null) { String name = entry.getName(); boolean notInFiles = true; for (File f : files) { if (f.getName().equals(name)) { notInFiles = false; break; } } if (notInFiles) { // Add ZIP entry to output stream. out.putNextEntry(new ZipEntry(name)); // Transfer bytes from the ZIP file to the output file int len; while ((len = zin.read(buf)) > 0) { out.write(buf, 0, len); } } entry = zin.getNextEntry(); } // Close the streams zin.close(); // Compress the files for (int i = 0; i < files.length; i++) { InputStream in = new FileInputStream(files[i]); // Add ZIP entry to output stream. out.putNextEntry(new ZipEntry(files[i].getName())); // Transfer bytes from the file to the ZIP file int len; while ((len = in.read(buf)) > 0) { out.write(buf, 0, len); } // Complete the entry out.closeEntry(); in.close(); } // Complete the ZIP file out.close(); tempFile.delete(); }
http://stackoverflow.com/questions/3048669/how-can-i-add-entries-to-an-existing-zip-file-in-java?lq=1