This question already has answers here:
Closed 11 years ago.
Possible Duplicate:
Appending files to a zip file with Java
I have a zip that contains a few folders in it but the important one is dir and inside that is another folder called folder and folder contains a lot of files that I need to be able to update.
I have now a dir outside of the zip called dir and in that is folder with the files i need to update in so the paths are the same. How can i update those files into the zip?
The tricky part is that dir is at the root of the zip and it contains a lot of folders not just folder but i only need to update the files in folder i can't mess with any of the files out side of folders but still in dir.
Can this be done? I know this can be done in bash using the -u modifier but I would prefer to do this with java if it's possible.
Thank you for any help with this issue
Just to be clearer
Inside Zip
/dir/folder/filestoupdate
Outside the zip
/dir/folder/filestomoveintozip
Alright well here is the final method it's the same method i pastebinned before which i actually got from the stackoverflow topic in the link #Qwe posted before but i added the path variable so that it could add files to folders inside the zip
Alright so now how to use it in my example above i wanted to add a file into a folder that was inside another folder i would do that using my setup in the question like this
private void addFilesToZip(File source, File[] files, String path){
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[4096];
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(path + 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()){
if(!zipEntryMatch(ze.getName(), files, path)){
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();
}
}
private boolean zipEntryMatch(String zeName, File[] files, String path){
for(int i = 0; i < files.length; i++){
if((path + files[i].getName()).equals(zeName)){
return true;
}
}
return false;
}
Thanks for the link ended up being able to improve that method a bit so that it could add in files that weren't in the root and now i'm a happy camper :) hope this helps someone else out as well
EDIT
I worked a bit more on the method so that it could not only append to the zip but it also is able to update files within the zip
Use the method like this
File[] files = {new File("/path/to/file/to/update/in")};
addFilesToZip(new File("/path/to/zip"), files, "folder/dir/");
You wouldn't start the path (last variable) with / as that's not how it's listed in the zip entries
Unfortunately, Java can't update Zip files. The request to enhance that was submitted 14 years ago ;-)
You will need to unpack it to a temp folder, add files there and pack it back again.
Related
I'm having the problem of replacing or updating some files within a certain directory inside a jar file.
I've read a few post already. The code (the JarUpdater Class) given at this link Updating .JAR's contents from code
is being very helpful for me to understand the role and the use of ZipInputStream, ZipOutputStream and ZipEntry, etc..
However, when I run it,
I have an EOF Exception
[EDITED by mk7: I found out the jar file was corrupted after I went through it 20 times or so. So after I replaced the jar file with a new one, the EOF Exception went away. The other two problems below still remains unsolved]
these two new xml files only get copied to the "root directory" of the jar file.
these two new xml files NEVER replaced the two original files inside a directory called /conf.
Which lines of code should I change in order to replace the xml files with the new ones?
With the System.out.println, I did see that the while loop steps through every directory and compare at every file as expected. A new temp jar was also created as expected...
I thought the statement "notInFiles = false" would take care of my need but it's NOT.
How do I step into the /conf and only replace those two files and NOT leave a copy at the root of the jar file?
What am I missing? Thanks for any insight!
Below are the code from that link.
import java.io.*;
import java.util.zip.ZipEntry;
import java.util.zip.ZipInputStream;
import java.util.zip.ZipOutputStream;
public class JarUpdater {
public static void main(String[] args) {
File[] contents = {new File("abc.xml"),
new File("def.xml")};
File jarFile = new File("xyz.jar");
try {
updateZipFile(jarFile, contents);
} catch (IOException e) {
e.printStackTrace();
}
}
public static void updateZipFile(File jarFile,
File[] contents) throws IOException {
// get a temp file
File tempFile = File.createTempFile(jarFile.getName(), null);
// delete it, otherwise you cannot rename your existing zip to it.
tempFile.delete();
System.out.println("tempFile is " + tempFile);
boolean renameOk=jarFile.renameTo(tempFile);
if (!renameOk)
{
throw new RuntimeException("could not rename the file "+jarFile.getAbsolutePath()+" to "+tempFile.getAbsolutePath());
}
byte[] buf = new byte[1024];
ZipInputStream zin = new ZipInputStream(new FileInputStream(tempFile));
ZipOutputStream out = new ZipOutputStream(new FileOutputStream(jarFile));
ZipEntry entry = zin.getNextEntry();
while (entry != null) {
String name = entry.getName();
boolean notInFiles = true;
for (File f : contents) {
System.out.println("f is " + f);
if (f.getName().equals(name)) {
// that file is already inside the jar file
notInFiles = false;
System.out.println("file already inside the jar file");
break;
}
}
if (notInFiles) {
System.out.println("name is " + name);
System.out.println("entry is " + entry);
// 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 contents
for (int i = 0; i < contents.length; i++) {
InputStream in = new FileInputStream(contents[i]);
// Add ZIP entry to output stream.
out.putNextEntry(new ZipEntry(contents[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();
}
}
In your first cycle (while loop) where you copy the entries which you don't want to replace you don't close the entries in the output zip file. Add out.closeEntry(); like this:
// 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);
}
// ADD THIS LINE:
out.closeEntry();
Also when you check if an entry is to be replaced, you should compare it to a full path, not just to the name of the file. For example if you want to replace abc.xml which is in the /conf folder, you should compare the entry name to "/conf/abc.xml" and not to "abc.xml".
To properly check if an entry is to be replaced:
String name = entry.getName();
boolean notInFiles = true;
for (File f : contents) {
System.out.println("f is " + f);
if (name.equals("/conf/" + f.getName()) {
// that file is already inside the jar file
notInFiles = false;
System.out.println("file already inside the jar file");
break;
}
}
And when you add the entries to the output which are the replaced files, you also have to specify the entry name having full path, e.g. "/conf/abc.xml" and not just "abc.xml" because it would put "abc.xml" in the root of the output zip.
To do this, start the entry name with "/conf/" like this:
// Add ZIP entry to output stream.
out.putNextEntry(new ZipEntry("/conf/" + contents[i].getName()));
For URIs with the protocol jar:file: (usable for all zip files), you can use a zip file system.
URI jarUri = new URI("jar:" + jarFile.toURI().toString()); // "jar:file:/C:/../xyz.jar"
Map<String, String> zipProperties = new HashMap<>();
zipProperties.put("encoding", "UTF-8");
try (FileSystem zipFS = FileSystems.newFileSystem(jarUri, zipProperties)) {
for (File file : contents) {
Path updatePath = zipFS.getPath("/" + file.getName());
Files.delete(updatePath);
Files.copy(file.toPath(), updatePath, StandardCopyOption.REPLACE_EXISTING);
}
} // closes.
One way to derive the URI is prefixing "jar:" to a File.toURI().
This is a bit more elegant and abstract, and also allows Files.copy in and out the zip. Something to keep in ones tool chest.
Hi i am new to java and i want to know how to access the files in the folder.
My requirement is that,
I am successfully compressing an image files and then writing it into a temporary file.
Now i want to access each file in the folder so that i can check the size of the files individually to perform some operations.
Please tell me how to access each file in the folder dynamically.
This is the code to compress and then access the folder.
BufferedImage resizeImageBmp = resizeImage(originalImage, type);
ImageIO.write(resizeImageBmp, "png", new File(tempDir + roots[i].getName())); //Compressing the file
String Temp = tempDir; //Path to the temporary folder
//Temp1 = ((Temp.length() / 1024)/1024);
if((Temp.length()) > 6){
//writeToDir(Temp,failureDir);
//System.out.println(Temp);
System.out.println(Temp);
}
else{
//System.out.println(Temp);
System.out.println(Temp);
}
Consider using File.listFiles() listed here.
File tempFile = new File(tempDir);
File[] allFilesInTempDir = tempFile.listFiles();
I can't seem to import the packages needed or find any online examples of how to extract a .tar.gz file in java.
What makes it worse is I'm using JSP pages and am having trouble importing packages into my project. I'm copying the .jar's into WebContent/WEB-INF/lib/ and then right clicking on the project and selecting import external jar and importing it. Sometimes the packages resolve, other times they don't. Can't seem to get GZIP to import either. The imports in eclipse for jsp aren't intuitive like they are in normal Java code where you can right click a recognized package and select import.
I've tried the Apache commons library, the ice and another one called JTar. Ice has imported, but I can't find any examples of how to use it?
I guess I need to uncompress the gzipped part first, then open it with the tarstream?
Any help is greatly appreciated.
The accepted answer works fine, but I think it is redundant to have a write to file operation.
You could use something like
TarArchiveInputStream tarInput =
new TarArchiveInputStream(new GZipInputStream(new FileInputStream("Your file name")));
TarArchiveEntry currentEntry = tarInput.getNextTarEntry();
while(currentEntry != null) {
File f = currentEntry.getFile();
// TODO write to file as usual
}
Hope this help.
Maven Repo
Ok, i finally figured this out, here is my code in case this helps anyone in the future.
Its written in Java, using the apache commons io and compress librarys.
File dir = new File("directory/of/.tar.gz/files/here");
File listDir[] = dir.listFiles();
if (listDir.length!=0){
for (File i:listDir){
/* Warning! this will try and extract all files in the directory
if other files exist, a for loop needs to go here to check that
the file (i) is an archive file before proceeding */
if (i.isDirectory()){
break;
}
String fileName = i.toString();
String tarFileName = fileName +".tar";
FileInputStream instream= new FileInputStream(fileName);
GZIPInputStream ginstream =new GZIPInputStream(instream);
FileOutputStream outstream = new FileOutputStream(tarFileName);
byte[] buf = new byte[1024];
int len;
while ((len = ginstream.read(buf)) > 0)
{
outstream.write(buf, 0, len);
}
ginstream.close();
outstream.close();
//There should now be tar files in the directory
//extract specific files from tar
TarArchiveInputStream myTarFile=new TarArchiveInputStream(new FileInputStream(tarFileName));
TarArchiveEntry entry = null;
int offset;
FileOutputStream outputFile=null;
//read every single entry in TAR file
while ((entry = myTarFile.getNextTarEntry()) != null) {
//the following two lines remove the .tar.gz extension for the folder name
String fileName = i.getName().substring(0, i.getName().lastIndexOf('.'));
fileName = fileName.substring(0, fileName.lastIndexOf('.'));
File outputDir = new File(i.getParent() + "/" + fileName + "/" + entry.getName());
if(! outputDir.getParentFile().exists()){
outputDir.getParentFile().mkdirs();
}
//if the entry in the tar is a directory, it needs to be created, only files can be extracted
if(entry.isDirectory){
outputDir.mkdirs();
}else{
byte[] content = new byte[(int) entry.getSize()];
offset=0;
myTarFile.read(content, offset, content.length - offset);
outputFile=new FileOutputStream(outputDir);
IOUtils.write(content,outputFile);
outputFile.close();
}
}
//close and delete the tar files, leaving the original .tar.gz and the extracted folders
myTarFile.close();
File tarFile = new File(tarFileName);
tarFile.delete();
}
}
I am trying to unzip a zip file which is stored in the raw folder. Code is as follows
try
{
File myDir = new File(getFilesDir().getAbsolutePath());
File newFile = new File(myDir + "/imageFolder");
if(!newFile.exists())
{
newFile.mkdir();
}
ZipInputStream zipIs = new ZipInputStream(con
.getResources().openRawResource(R.raw.images));
ZipEntry ze = null;
while ((ze = zipIs.getNextEntry()) != null)
{
Log.v("Name", ze.getName());
Log.v("Size", "" + ze.getSize());
if(ze.getSize() >0)
{
FileOutputStream fout = new FileOutputStream(newFile
+ "/" + ze.getName());
byte[] buffer = new byte[1024];
int length = 0;
while ((length = zipIs.read(buffer)) > 0)
{
fout.write(buffer, 0, length);
}
zipIs.closeEntry();
fout.close();
}
}
zipIs.close();
} catch (Exception e)
{
e.printStackTrace();
}
But I keep getting this error
01-18 11:24:28.301: W/System.err(2285): java.io.FileNotFoundException:
/data/data/com.example.ziptests/files/imageFolder/TestImages/background.png
(Not a directory)
I have absolutely no idea why it is causing this, it finds the files, but when it comes to writing them out, it brings up that error. Originally I found a problem that was caused by having the zip file zipped up on the mac, so I zipped up the file on my windows machine instead, that got rid of one problem (when you zip on a mac, it adds these extra folders and files such s store.ds which causes an error when trying to unzip), but this not a directory error keeps coming up.
Any ideas why this is happening?
Please try below link code for unzip zip file.
Code for Extract Zip File
Unzip Zip File
The Problem is I am Uploading zip File which is not made using winrar software, so it is not proper extracted and give me error.
It will solve your problem.
You can't write files to the raw folder. It is a read only dir intended to contain resource files included in your apk.
UPDATE
That file would be better in the assets directory. You can access it through the AssetManager. If not, leave it in the res/raw dir, but access it through Resources.openRawResource. Either way they are Read-Only.
Im using the following Code to try an package a directory with all its subdirectories and files into one jar-file.
private void writeFile(JarOutputStream jos, File f) throws IOException {
byte buffer[] = new byte[BUFFER_SIZE];
if (f == null || !f.exists())
return; //
JarEntry jarAdd = new JarEntry(f.getName());
jarAdd.setTime(f.lastModified());
jos.putNextEntry(jarAdd);
if(f.isDirectory()){
File[] files = f.listFiles();
for(int i = 0; i < files.length ; i++){
writeFile(jos,files[i]);
}
}
else{
FileInputStream in = new FileInputStream(f);
while (true) {
int nRead = in.read(buffer, 0, buffer.length);
if (nRead <= 0)
break;
jos.write(buffer, 0, nRead);
}
in.close();
}
}
However FileInputStream does not work on directories, so i assume adding the JarEntry will suffice.
Since I do have another problem, I am not able to see if this assumption is correct.
This problem is that many entries made are duplicate.
When trying to add the structure...
activation
/--activation
/-- somefile.txt
...this code fails, because activation is added twice to the jar.
Since those are different directories nested within each other I do not see why this should not work in some way.
It seems that adding the entry using file.getName() will get entry-ids that do not
differentiate between the directories those files are in.
However when I use
file.getAbsolutePath()
for example, the jar structure is messed up with
directories like "C_" and the contained files are not readable anymore.
Can anyone recommend a way to package directory structures in a jar file correctly?
Note that a JarEntry representing a directory (folder) must end with a slash ("/") in order to be treated as such; otherwise it will be considered a plain file.
JarEntry dir = new JarEntry("activation/");
The name of the entry should be a relative path from the root of your tree. If you have
a
-- b
------c.txt
the entries should be a/, a/b/ and a/b/c.txt. Add a prefix argument to you recursive method (which is the empty string initially), and each time a directory is met, call the method with the current prefix + the directory name appended + /. And always prepend the prefix to the file name to compose the entry name.