I am trying to generate a zip file in Java, that contains several files of different types (e.g. images, fonts etc) that are lying in different locations. Furthermore I want the zip file to have subfolders where the files are put by their type (e.g. images should go to the images folder within the zip.
These are the files that I have (each can be in a different location):
index.html
img1.jpg
img2.jpg
font1.woff
font2.woff
style.css
custom.js
And this is how they should be in the zip file:
index.html
images/img1.jpg
images/img2.jpg
fonts/font1.woff
fonts/font2.woff
js/custom.js
css/styles.css
So far I have managed to take one file in a specific path and prompt the user for the output location. A zip-file will be generated with the file that is specified in the input. Here is the code I have so far:
JFrame parentFrame = new JFrame();
JFileChooser fileChooser = new JFileChooser();
fileChooser.setDialogTitle("Speicherort auswählen");
int userSelection = fileChooser.showSaveDialog(parentFrame);
String pathToFile;
if (userSelection == JFileChooser.APPROVE_OPTION) {
File fileToSave = fileChooser.getSelectedFile();
print(fileToSave.getAbsolutePath());
pathToFile = fileToSave.getAbsolutePath();
}
pathToFile = pathToFile.replace("\\", "/");
String outFileName = pathToFile;
String inFileName = "C:/Users/asoares/Desktop/mobio_export_test/index.html";
ZipOutputStream zos = null;
FileInputStream fis = null;
try {
zos = new ZipOutputStream(new FileOutputStream(outFileName));
fis = new FileInputStream(inFileName);
zos.putNextEntry(new ZipEntry(new File(inFileName).getName()));
int len;
byte[] buffer = new byte[2048];
while((len = fis.read(buffer, 0, buffer.length)) > 0) {
zos.write(buffer, 0, len);
}
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
} finally {
if(fis != null){
try {
fis.close();
} catch (IOException e) {}
}
if(zos != null){
try {
zos.closeEntry();
zos.close();
} catch (IOException e) {}
}
}
I would be really glad if someone can help me!!!
It should work like this.
The zip directory name should at best be created by another method (there are more image types than jpg :)).
public static Path zip(List<Path> files, Path zipFileTarget) throws IOException {
try (FileOutputStream fos = new FileOutputStream(zipFileTarget.toFile());
ZipOutputStream zos = new ZipOutputStream(fos)) {
if (!Files.exists(zipFileTarget))
Files.createFile(zipFileTarget);
createEntries(files, zos);
zos.close();
return zipFileTarget;
}
}
private static List<String> createEntries(List<Path> files, ZipOutputStream zos) throws IOException {
List<String> zippedFiles = new ArrayList<>();
Matcher matcherFileExt = Pattern.compile("^.*\\.([^.]+)$").matcher("");
for (Path f : files) {
if (Files.isRegularFile(f)) {
String fileName = f.getFileName().toString();
String fileExt = matcherFileExt.reset(fileName).matches()
? matcherFileExt.replaceAll("$1")
: "unknown";
// You should determine the dir name with a more sophisticated
// approach.
String dir;
if (fileExt.equals("jpg")) dir = "images";
else if (fileExt.equals("woff")) dir = "fonts";
else dir = fileExt;
zos.putNextEntry(new ZipEntry(dir + "/" + fileName));
Files.copy(f, zos);
zippedFiles.add(fileName);
}
}
return zippedFiles;
}
Edit: this approach works with java 1.7+. You can easily convert a File object to a Path object by calling its toPath() method.
Related
I am trying to delete a zip file after unziping. but I am not able to delete it:
if (file.getName().contains(".zip")) {
System.out.println(file.getAbsolutePath()); // I am getting the correct path
file.delete();
System.out.println(file.getName()); // I am getting the correct name Script-1.zip
}
This is the full code
public class Zip4 {
public static void main(String[] args) {
File[] files = new File(args[0]).listFiles();
for(File file : files)
// System.out.println(file.getName());
//if(file.getName().contains("1400") && file.getName().contains(".zip"))
extractFolder(args[0] + file.getName(), args[1]);
DeleteFiles();
// for(File file : files)
// System.out.println("File:C:/1/"+ file.getName());
// extractFolder(args[0]+file.getName(),args[1]);
}
private static void DeleteFiles()
{
File f = null;
File[] paths;
f = new File("D:/Copyof");
paths = f.listFiles();
for(File path:paths)
{
// prints file and directory paths
if(path.getName().contains("J14_0_0RC") || path.getName().contains(".zip") || path.getName().contains(".log"))
{
//System.out.println(path);
path.delete();
}
}
}
private static void extractFolder(String zipFile,String extractFolder)
{
try
{
int BUFFER = 2048;
File file = new File(zipFile);
ZipFile zip = new ZipFile(file);
String newPath = extractFolder;
new File(newPath).mkdir();
Enumeration zipFileEntries = zip.entries();
// Process each entry
while (zipFileEntries.hasMoreElements())
{
// grab a zip file entry
ZipEntry entry = (ZipEntry) zipFileEntries.nextElement();
String currentEntry = entry.getName();
File destFile = new File(newPath, currentEntry);
//destFile = new File(newPath, destFile.getName());
File destinationParent = destFile.getParentFile();
// create the parent directory structure if needed
destinationParent.mkdirs();
if (!entry.isDirectory())
{
BufferedInputStream is = new BufferedInputStream(zip
.getInputStream(entry));
int currentByte;
// 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
while ((currentByte = is.read(data, 0, BUFFER)) != -1) {
dest.write(data, 0, currentByte);
}
dest.flush();
dest.close();
fos.flush();
fos.close();
is.close();
}
}
if(file.getName().contains(".zip"))
{
System.out.println(file.getAbsolutePath());
file.delete();
System.out.println(file.getName());
}
}
catch (Exception e)
{
System.out.println("Error: " + e.getMessage());
}
}
}
ZipFile is a closeable resource. So either close() it once you're done in a finally block or create it with try-with-resources (since java7):
try(ZipFile zip = new ZipFile(file)){
//unzip here
}
file.delete();
Apart from this, you should revisit this block
dest.flush();
dest.close();
fos.flush();
fos.close();
is.close();
which is quite prone to resource leaks. If one of the upper calls fails, all subsequent calls are not invoked, resulting in unclosed resources and resource leakage.
So best would be to use try-with-resources here, too.
try(BufferedInputStream is = new BufferedInputStream(zip.getInputStream(entry));
FileOutputStream fos = new FileOutputStream(destFile);
BufferedOutputStream dest = new BufferedOutputStream(fos, BUFFER)) {
//write the data
} //all streams are closed implicitly here
Or use an existing tool for that, for example Apache Commons IO IOUtil.closeQuietly(resource) or embedd every single call into
if(resource != null) {
try{
resource.close();
} catch(IOException e){
//omit
}
}
You could also omit the call to flush() which is done implicitly when closing the resource.
Using Java I have to read text files which are inside gz file which is in another .tar.gz
gz_ltm_logs.tar.gz is the filename. It then has files ltm.1.gz, ltm.2.gz inside it and then these files have text files in them.
I wanted to do it using java.util.zip.* only but if it is impossible then I can look at other libraries.
I thought I will be able to do it using java.util.zip. But doesn't seem straightforward
Here's some code to give you an idea. This method will try to extract a given tar.gz file to outputFolder.
public static void extract(File input, File outputFolder) throws IOException {
byte[] buffer = new byte[1024];
GZIPInputStream gzipFile = new GZIPInputStream(new FileInputStream(input));
ByteOutputStream tarStream = new ByteOutputStream();
int gzipLengthRead;
while ((gzipLengthRead = gzipFile.read(buffer)) > 0){
tarStream.write(buffer, 0, gzipLengthRead);
}
gzipFile.close();
org.apache.tools.tar.TarInputStream tarFile = null;
// files inside the tar
OutputStream out = null;
try {
tarFile = new org.apache.tools.tar.TarInputStream(tarStream.newInputStream());
tarStream.close();
TarEntry entry = null;
while ((entry = tarFile.getNextEntry()) != null) {
String outFilename = entry.getName();
if (entry.isDirectory()) {
File directory = new File(outputFolder, outFilename);
directory.mkdirs();
} else {
File outputFile = new File(outputFolder, outFilename);
File outputDirectory = outputFile.getParentFile();
if (!outputDirectory.exists()) {
outputDirectory.mkdirs();
}
out = new FileOutputStream(outputFile);
// Transfer bytes from the tarFile to the output file
int innerLen;
while ((innerLen = tarFile.read(buffer)) > 0) {
out.write(buffer, 0, innerLen);
}
out.close();
}
}
} finally {
if (tarFile != null) {
tarFile.close();
}
if (out != null) {
out.close();
}
}
}
I have two jar files. Normally if I want to 'unpack' resource from my jar file I go for :
InputStream in = MyClass.class.getClassLoader().getResourceAsStream(name);
byte[] buffer = new byte[1024];
int read = -1;
File temp2 = new File(new File(System.getProperty("user.dir")), name);
FileOutputStream fos2 = new FileOutputStream(temp2);
while((read = in.read(buffer)) != -1) {
fos2.write(buffer, 0, read);
}
fos2.close();
in.close();
What If I would have another JAR files in the same directory? Can I access the second JAR file resources in simillar way? This second JAR is not runned so don't have own class loader. Is the only way to unzip this second JAR file?
I've used the below mentioned code to do the same kind of operation. It uses JarFile class to do the same.
/**
* Copies a directory from a jar file to an external directory.
*/
public static void copyResourcesToDirectory(JarFile fromJar, String jarDir, String destDir)
throws IOException {
for (Enumeration<JarEntry> entries = fromJar.entries(); entries.hasMoreElements();) {
JarEntry entry = entries.nextElement();
if (entry.getName().startsWith(jarDir + "/") && !entry.isDirectory()) {
File dest = new File(destDir + "/" + entry.getName().substring(jarDir.length() + 1));
File parent = dest.getParentFile();
if (parent != null) {
parent.mkdirs();
}
FileOutputStream out = new FileOutputStream(dest);
InputStream in = fromJar.getInputStream(entry);
try {
byte[] buffer = new byte[8 * 1024];
int s = 0;
while ((s = in.read(buffer)) > 0) {
out.write(buffer, 0, s);
}
} catch (IOException e) {
throw new IOException("Could not copy asset from jar file", e);
} finally {
try {
in.close();
} catch (IOException ignored) {}
try {
out.close();
} catch (IOException ignored) {}
}
}
}
If the other Jar is in your regular classpath then you can simply access the resource in that jar in the exact same way. If the Jar is just an file that's not on your classpath you will have to instead open it and extract the file with the JarFile and related classes. Note that Jar files are just special types of Zip files, so you can also access a Jar file with the ZipFile related classes
You can use URLClassLoader.
URLClassLoader classLoader = new URLClassLoader(new URL[]{new URL("path_to_file//myjar.jar")})
classLoader.loadClass("MyClass");//is requared
InputStream stream = classLoader.getResourceAsStream("myresource.properties");
I am creating a zip file and downloading it. I am able to download all file files with out any error. But the files in the zip are placing in nested folders according to the files real path like C>Users>workspace>.metadata>.plugins>org.eclipse.wst.server.core>tmp0>wtpwebapps>finaldebrief while I am trying to zip 'finaldebrief' folder only. I want to get 'finaldebrief' folder directly in zip file. can someone help me out. Thanks in Advance.
Here is my code
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
int workshopid = Integer.parseInt(request.getParameter("currentworkshopid"));
javax.servlet.ServletContext context = getServletConfig().getServletContext();
String path = context.getRealPath("finaldebrief");
try
{
response.setContentType("application/zip");
response.setHeader("Content-Disposition", "attachment;filename=finaldebrief.zip");
ZipOutputStream zos = new
ZipOutputStream(response.getOutputStream());
zipDir(path + "/", zos);
zos.close();
}
catch(Exception e)
{
e.printStackTrace();
}
}
public void zipDir(String dir2zip, ZipOutputStream zos)
{
try
{
//create a new File object based on the directory we have to zip File
File zipDir = new File(dir2zip);
//get a listing of the directory content
String[] dirList = zipDir.list();
byte[] readBuffer = new byte[2156];
int bytesIn = 0;
//loop through dirList, and zip the files
for(int i=0; i<dirList.length; i++)
{
File f = new File(zipDir, dirList[i]);
if(f.isDirectory())
{
//if the File object is a directory, call this
//function again to add its content recursively
String filePath = f.getPath();
// String filePath = f.getCanonicalPath();
zipDir(filePath, zos);
//loop again
continue;
}
//if we reached here, the File object f was not a directory
//create a FileInputStream on top of f
FileInputStream fis = new FileInputStream(f);
// create a new zip entry
ZipEntry anEntry = new ZipEntry(f.getPath());
//place the zip entry in the ZipOutputStream object
zos.putNextEntry(anEntry);
//now write the content of the file to the ZipOutputStream
while((bytesIn = fis.read(readBuffer)) != -1)
{
zos.write(readBuffer, 0, bytesIn);
}
//close the Stream
fis.close();
}
}
catch(Exception e)
{
e.printStackTrace();
}
}
Within the line
ZipEntry anEntry = new ZipEntry(f.getPath());
you specify the path inside your zip file (i.e. f.getPath()). If you want a shorter path or none at all just manipulate this part (e.g. f.getName()).
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.