Link to home
Start Free TrialLog in
Avatar of jimmy282
jimmy282Flag for United Kingdom of Great Britain and Northern Ireland

asked on

Zip file Extraction and Insertion ?

Hi Experts,

I am trying to make a simple program in Java.
I want my program to accept few parameters .

1) Path
2) filename

With these two parameters , I want to make a zip file (with name as specified filename) and this zip file will contain all the files in the path that is specified.

The files can be txt files,htm files,binary files or any other file.
Remember that path can also be a directory ....so we will have to zip the subdirectories and their contents also.
I want all of them to be zipped by my program.

In other words, I would like to do the same thing that Winzip does.

I have been trying to use java.util.zip package, but I am unable to get this thing to work.

I have got another problem.
I know how to read files from a zip file(programmatically) but how do I extract files from a zip file.I mean I want to read a zip file and extract all the files in that zip file to the location specified.

I am ready to up the points if I get a nice solution for this.

Please help me.

Jimmy
Avatar of yongsing
yongsing

Listening
Hi jimmy,
     Each zip file has a header with the info such as the name of the file and the compression method that was used. In java u can use ZipInputStream to read a zip file. IN this class there is a method getNextEntry and it returns an object of type ZipEntry that describes that entry. The read method of the ZipInputStream returns -1 at the end of the current entry. Using this method u can iterate through all the entries in the zip file. getName method  of ZipEntry gives u the name ( with relative path ) of the file. So u can see the path separator delimiter and can find out where it should reside in the file system relative to the current position, and then u can create directories and store the file at the apropriate location.
     Here is a program that i have taken from core java book, This program will dislay u all the entries in the zip file in a drop down list and on double clicking an entry u will get the contents of the file. So here is the progam:

/**
 * @version 1.20 17 Aug 1998
 * @author Cay Horstmann
 */

import java.awt.*;
import java.awt.event.*;
import java.io.*;
import java.util.*;
import java.util.zip.*;
import javax.swing.*;
import javax.swing.filechooser.FileFilter;

public class ZipTest extends JFrame
   implements ActionListener
{  public ZipTest()
   {  setTitle("ZipTest");
      setSize(300, 400);

      JMenuBar mbar = new JMenuBar();
      JMenu m = new JMenu("File");
      openItem = new JMenuItem("Open");
      openItem.addActionListener(this);
      m.add(openItem);
      exitItem = new JMenuItem("Exit");
      exitItem.addActionListener(this);
      m.add(exitItem);
      mbar.add(m);

      fileList.addActionListener(this);

      Container contentPane = getContentPane();
      contentPane.add(mbar, "North");
      contentPane.add(fileList, "South");
      contentPane.add(fileText, "Center");
   }

   public void actionPerformed(ActionEvent evt)
   {  Object source = evt.getSource();
      if (source == openItem)
      {  JFileChooser chooser = new JFileChooser();
         chooser.setCurrentDirectory(new File("."));
         chooser.setFileFilter(new FileFilter()
            {  public boolean accept(File f)
               {  return f.getName().toLowerCase()
                     .endsWith(".zip")
                     || f.isDirectory();
               }
               public String getDescription()
               { return "ZIP Files"; }
            });
         int r = chooser.showOpenDialog(this);
         if (r == JFileChooser.APPROVE_OPTION)
         {  zipname = chooser.getSelectedFile().getPath();
            scanZipFile();
         }
      }
      else if (source == exitItem) System.exit(0);
      else if (source == fileList)
         loadZipFile((String)fileList.getSelectedItem());
   }

   public void scanZipFile()
   {  fileList.removeAllItems();
      try
      {  ZipInputStream zin = new ZipInputStream(new
            FileInputStream(zipname));
         ZipEntry entry;
         while ((entry = zin.getNextEntry()) != null)
         {  fileList.addItem(entry.getName());
            zin.closeEntry();
         }
         zin.close();
      }
      catch(IOException e) {}
   }

   public void loadZipFile(String name)
   {  try
      {  ZipInputStream zin = new ZipInputStream(new
            FileInputStream(zipname));
         ZipEntry entry;
         fileText.setText("");
         while ((entry = zin.getNextEntry()) != null)
         {  if (entry.getName().equals(name))
            {  BufferedReader in = new BufferedReader(new
                  InputStreamReader(zin));
               String s;
               while ((s = in.readLine()) != null)
                  fileText.append(s + "\n");
            }
            zin.closeEntry();
         }
         zin.close();
      }
      catch(IOException e) {}
   }

   public static void main(String[] args)
   {  Frame f = new ZipTest();
      f.show();
   }

   private JComboBox fileList = new JComboBox();
   private JTextArea fileText = new JTextArea();
   private JMenuItem openItem;
   private JMenuItem exitItem;
   private String zipname;
}

Hope this will help u.

Thanks
Pandey.
Avatar of jimmy282

ASKER

Pandey,
Thanks a lot for you program.
But that is not actually what I want.
My main purpose is not to read a zip file but to create a zip file.
Suppose I want location parameters to my java code.
filename and path and location
Lets say I pass 1)jimmy.zip and 2) c:\jdk1.3 and 3) d:\jimmy (optional) as parameters.

Then my Java code should create a zip file in the folder d:\jimmy with the contents of c:\jdk1.3 .
Well , I can do this much but the thing which I am unable to do is that I cant iterate over all the directories and subdirectories in the "path" parameter and put them in the zip file. One more problem is that if there is any image or any other binary file in the c:\jdk.13 folder , then I have to read that file and insert that too in the zip file.

I hope u  get my problem and will come up with a solution.

Jimmy
Jimmy,

You can try the following code:

import java.io.*;
import java.util.*;
import java.util.zip.*;

public class FileList
{

     public static void addDirectoryContents(String fileName, java.util.ArrayList out) throws Exception
     {

          File file1 = new File(fileName);
File file = file1.getAbsoluteFile();

          if (file.isDirectory())
          {
               String arr[] = file.list();
               for (int i=0;i < arr.length;i++)
               {
                    File subFile = new File(arr[i]);
                    if (subFile.isDirectory())
                    {
                         System.out.println("Directory: " + arr[i]);
                         addDirectoryContents(arr[i],out);
                    }
                    else
                    {
                         out.add(new File(arr[i]).getAbsolutePath());
                    }
               }
          }
          else
          {
               out.add(new File(fileName).getAbsolutePath());
          }
     }

public static void main(String args[]) throws Exception
{
ZipOutputStream zin = new ZipOutputStream(new
FileOutputStream("my.zip"));

java.util.ArrayList arr = new java.util.ArrayList();
FileList.addDirectoryContents(args[0],arr);
for (int i=0;i<arr.size();i++)
{
     System.out.println(arr.get(i));
     String str = (String) arr.get(i);

     ZipEntry ze = new ZipEntry(str);
     zin.putNextEntry(ze);

}
zin.close();

}


}

It creates a Zip file and add entries according to the command line arguments passed. If it is a file, the file is added as an entry. If it is a directory, the contents of the directory are added. It is also working fine for executables, graphics and binary files. It has a limitation though. In case of a Directory, the contents of the subfolders are not added. Will be able to figure it out soon.

sghosh
Many thanks sghosh,

I will be very glad to award you with 250 or 300 points if u are able to figure out that for me.

Because I need it very urgently.
And we need to add the contents of subdirectories also.


Also can u give such a code for file extraction....I mean I dont want to read the contents of the file.That I have done.I want to actually extract the files from the zip file and place them in the same directory structure as they were inside the zip file.

Waiting for answer.

Jimmy


P.S
There can be any no. of subdirectories. We need to parse all of them.

Jimmy
sghosh:
One more question for you.
OK just suppose we get everything done. I mean zip file creation and extraction the way I want.

After that , how can I optimize this code.
I mean if I pass a huge directory as parameter to this code, Then this will timeout according to me because I have to run this code in a browser.
So can we make it a multithreaded program.
I mean one thread reading contents of directory....other thread putting them in the zip file simultaneously.

Can this be achieved??

If yes, how???

Jimmy
Hi Jimmy,
   Here is a pseudo code that u can use for iterating over the files in the directory, using recursion. U can use the class File of java.io to do this. First u get the list of the files using list method and then check if this is a file then read it and put it in the zip file. If the file is a directory then call the function recursively.
   Also one note treat all files as binary and don't distingish between the different kind of files for reading.

I hope this will help u.

-Pandey.



import java.io.*;

public static FindAllFiles
{
  public static void main(String args[])
  {
     File pathName = new File(args[0]); // the path u want to extract
     String fileNames = pathName.list(); // this will give u all the files+directories in this path
     for (int i=0; i<fileNames.length; i++) //iterate over all files
     {
       File currentFile = new File(pathName.getPath(), fileNames[i]; // this will give the complete file name with full path of current file
       if (currentFile.isFile()) // if the current file is a file
       {
           /* here open the file in the binary mode
              and read it and put it in the zip file
           */
       }
       if (currentFile.isDirectory()) // if current file is a directory
       {
          main( new String[] { currentFile.getPath() });
          /* in the step above if the current file
             is a directory then we are using RECURSION
             to get all the files of this directory
          */
       }
     }

  }
}
Thanks Pandey,
I will try

Jimmy
Jimmy,

Here is the code. It adds the contents of directories and subdirectories of the argument being passed.

import java.io.*;
import java.util.*;
import java.util.zip.*;

public class FileList
{

     public static void addDirectoryContents(File fileIn, java.util.ArrayList out) throws Exception
     {

         
          File file = fileIn;
         
         
          if (file.isDirectory())
          {
               
               String arr[] = file.list();
               for (int i=0;i < arr.length;i++)
               {
                    File subFile = new File(file.getAbsolutePath(), arr[i]);
                    if (subFile.isDirectory())
                    {
                         
                         addDirectoryContents(subFile,out);
                    }
                    else
                    {
                         
                                                  out.add(subFile.getAbsolutePath() );


                    }
               }
          }
          else
          {
               out.add(fileIn.getAbsolutePath() );



          }
     }

public static void main(String args[]) throws Exception
{
ZipOutputStream zin = new ZipOutputStream(new
FileOutputStream("my.zip"));

java.util.ArrayList arr = new java.util.ArrayList();
FileList.addDirectoryContents(new File(args[0]).getAbsoluteFile(),arr);
for (int i=0;i<arr.size();i++)
{
     String str = (String) arr.get(i);

     ZipEntry ze = new ZipEntry(str);
     
     zin.putNextEntry(ze);

}
zin.close();

}


}

You can try the code in realtime environment and actually see the occurrence of time out for large directories.The Multithreaded approach seems to be achievable to me. Hope to come out with a complete solution soon.

sghosh
This is the code I amnaged to get to work.

import java.util.*;

import java.util.zip.*;

import java.text.*;

import java.io.*;
 
 

public class JZip

{

  static public

  void main(String[] args)

  throws java.io.IOException

  {

         if(args.length<2)

            usage();

         else if(args[0].startsWith("-t"))

              list(args[1]);

         else if(args[0].startsWith("-x"))

              unzip(args[1]);

         else if(args[0].startsWith("-c")) {

              if(args.length < 3)

                 usage();

              else {

                   String[] files = new String[args.length-2];

                   System.arraycopy(args, 2, files, 0, files.length);

                   zip(args[1], files);

              }

         } else

           usage();

  }
 
 

  private static void usage()

  {

           System.out.println("usage:");

           System.out.println("\tJZip -t <filename.zip>");

           System.out.println("\t\tshow zip contents");

           System.out.println("\tJZip -x <filename.zip>");

           System.out.println("\t\textract files");

           System.out.println("\tJZip -c <filename.zip> <files>..");

           System.out.println("\t\tcreate a zip with files");

  }
 
 

  private static

  void list(String filename)

  throws IOException

  {

    ZipFile zip = new ZipFile(filename);

    SimpleDateFormat fmt

        = new SimpleDateFormat("yyyy.mm.dd hh:mm:ss");

    Enumeration e = zip.entries();

    System.out.println("      Size |         Date        | Name");

    System.out.println("-----------+---------------------+--------------------------");

    while(e.hasMoreElements()) {

      ZipEntry entry = (ZipEntry)e.nextElement();

      String sz = "         "+entry.getSize();

      sz = sz.substring(sz.length() - 10);

      String tm = fmt.format(new Date(entry.getTime()));

      System.out.println(sz+" | "+tm+" | "+entry.getName());

    }

  }
 
 

  private static

  void unzip(String filename)

  throws IOException

  {

    final char sep = File.separatorChar;

    byte[] buff = new byte[1024];

    int m, n;

    // for each file in zip

    ZipFile zip = new ZipFile(filename);

    Enumeration e = zip.entries();

    while(e.hasMoreElements())

    {

      // get filename using local separator

      ZipEntry entry = (ZipEntry)e.nextElement();

      StringBuffer fixed = new StringBuffer(entry.getName());

      for( int i = 0; iif( fixed.charAt(i) == '/')

               fixed.setCharAt(i, sep);

      File file = new File(fixed.toString());
 
 

      // create dir

      if(entry.isDirectory()) {

        file.mkdirs();

        continue;

      }

      String dir = file.getParent();

      if( dir != null)

          new File(dir).mkdirs();
 
 

      // unzip file

      System.out.println("unzipping: "+file);

      OutputStream out = new FileOutputStream(file);

      InputStream in = zip.getInputStream(entry);

      while( (n = in.read(buff, 0, buff.length))!= -1)

               out.write(buff, 0 , n);

    }

  }
 
 

  private static

  String[] recurse(String[] files, String dir)

  {

   final char sep = File.separatorChar;

   Vector v = new Vector();

   File file;

   for(int i=0; i<files.length; ++i)

   {

    String filename = ( dir == null ? files[i]: dir+sep+files[i] );

    file = new File( filename );

    v.addElement(filename);

    if( file.isDirectory()){

        String[] res = recurse(file.list(), filename);

        for( int j = 0; j<res.length; ++j)

             v.addElement(res[j]);

    }

   }

    String[] res = new String[v.size()];

    v.copyInto(res);

    return res;

  }

  private static

  void zip(String filename, String[] files)

  throws IOException

  {

   InputStream in;

   int n;
 
 

   // check

   File zip = new File(filename);

   if(zip.exists()) {

      System.out.println("File "+filename+" already exist");

      System.exit(1);

   }

   // open out file and write input files

   String[] res = recurse(files, null);

   ZipOutputStream out = new ZipOutputStream(new FileOutputStream(zip));

   byte[] buff = new byte[1024];

   for( int i = 0; i<res.length; ++i) {

        File current = new File(res[i]);

        if( current.isDirectory())

            continue;

        System.out.println("zipping: "+current);

        out.putNextEntry(new ZipEntry(res[i]));

        in = new FileInputStream(current);

        while( (n = in.read(buff, 0, buff.length))!= -1)

               out.write(buff, 0 , n);

        in.close();

   }

   out.close();

  }

}

This code does the same as I want.
I would be very hapy if u could make it multithreaded.

Jimmy
Jimmy,

The following is the multithreaded version of your code.

In this approach, I have given the list(), zip() and unzip() functions a public access to be called from the run() method of thread.

In another approach, the functions can be made resident in the Thread only so that they can be privately accessed.

import java.util.*;

import java.util.zip.*;

import java.text.*;

import java.io.*;

// This is the background thread for handling all Zip and Unzip functions
class ZipThread extends Thread
{
String option = "";
String argument1 = "";
String argument2[] =null;
ZipThread(String option, String argument1)
{
this.option = option;
this.argument1 = argument1;
}
ZipThread(String option, String argument1, String[] argument2)
{
this.option = option;
this.argument1 = argument1;
this.argument2 = argument2;
}
public void run()
{
if(option.startsWith("-t"))

JZip.list(argument1);

else if(option.startsWith("-x"))

JZip.unzip(argument1);

else if(option.startsWith("-c"))  

JZip.zip(argument1, argument2);

}
}

public class JZip

{

static public

void main(String[] args)

throws java.io.IOException

{

if(args.length<2)

usage();

else if(args[0].startsWith("-t"))
{

ZipThread z1 = new ZipThread("-t", args[1]);
z1.start();
//list(args[1]);
}

else if(args[0].startsWith("-x"))
{

ZipThread z2 = new ZipThread("-x",args[1]);
//unzip(args[1]);
z2.start();
}

else if(args[0].startsWith("-c")) {

if(args.length < 3)

usage();

else {

String[] files = new String[args.length-2];

System.arraycopy(args, 2, files, 0, files.length);

ZipThread z3 = new ZipThread("-c", args[1], files);
z3.start();
//zip(args[1], files);

}

} else

usage();

}



private static void usage()

{

System.out.println("usage:");

System.out.println("\tJZip -t <filename.zip>");

System.out.println("\t\tshow zip contents");

System.out.println("\tJZip -x <filename.zip>");

System.out.println("\t\textract files");

System.out.println("\tJZip -c <filename.zip> <files>..");

System.out.println("\t\tcreate a zip with files");

}



public static

void list(String filename)

throws IOException

{

ZipFile zip = new ZipFile(filename);

SimpleDateFormat fmt

= new SimpleDateFormat("yyyy.mm.dd hh:mm:ss");

Enumeration e = zip.entries();

System.out.println(" Size | Date | Name");

System.out.println("-----------+---------------------+--------------------------");

while(e.hasMoreElements()) {

ZipEntry entry = (ZipEntry)e.nextElement();

String sz = " "+entry.getSize();

sz = sz.substring(sz.length() - 10);

String tm = fmt.format(new Date(entry.getTime()));

System.out.println(sz+" | "+tm+" | "+entry.getName());

}

}



public static

void unzip(String filename)

throws IOException

{

final char sep = File.separatorChar;

byte[] buff = new byte[1024];

int m, n;

// for each file in zip

ZipFile zip = new ZipFile(filename);

Enumeration e = zip.entries();

while(e.hasMoreElements())

{

// get filename using local separator

ZipEntry entry = (ZipEntry)e.nextElement();

StringBuffer fixed = new StringBuffer(entry.getName());

for( int i = 0; iif( fixed.charAt(i) == '/')

fixed.setCharAt(i, sep);

File file = new File(fixed.toString());



// create dir

if(entry.isDirectory()) {

file.mkdirs();

continue;

}

String dir = file.getParent();

if( dir != null)

new File(dir).mkdirs();



// unzip file

System.out.println("unzipping: "+file);

OutputStream out = new FileOutputStream(file);

InputStream in = zip.getInputStream(entry);

while( (n = in.read(buff, 0, buff.length))!= -1)

out.write(buff, 0 , n);

}

}



public static

String[] recurse(String[] files, String dir)

{

final char sep = File.separatorChar;

Vector v = new Vector();

File file;

for(int i=0; i<files.length; ++i)

{

String filename = ( dir == null ? files[i]: dir+sep+files[i] );

file = new File( filename );

v.addElement(filename);

if( file.isDirectory()){

String[] res = recurse(file.list(), filename);

for( int j = 0; j<res.length; ++j)

v.addElement(res[j]);

}

}

String[] res = new String[v.size()];

v.copyInto(res);

return res;

}

public static

void zip(String filename, String[] files)

throws IOException

{

InputStream in;

int n;



// check

File zip = new File(filename);

if(zip.exists()) {

System.out.println("File "+filename+" already exist");

System.exit(1);

}

// open out file and write input files

String[] res = recurse(files, null);

ZipOutputStream out = new ZipOutputStream(new FileOutputStream(zip));

byte[] buff = new byte[1024];

for( int i = 0; i<res.length; ++i) {

File current = new File(res[i]);

if( current.isDirectory())

continue;

System.out.println("zipping: "+current);

out.putNextEntry(new ZipEntry(res[i]));

in = new FileInputStream(current);

while( (n = in.read(buff, 0, buff.length))!= -1)

out.write(buff, 0 , n);

in.close();

}

out.close();

}

}


Hope that helps!
sghosh
Hi Jimmy,
    It is not difficult to make this program multithreaded.
    We can make the reading of the file a separate thread i.e. a separate thread will be spawned for all the file we are going to read. Do implement this just make one class which extends thread and implement all the file reading mechanism inside it.

Something like this:

Class ReadTheFile extends Thread
{
  public ReadTheFile(String i_fileName)
  {
     this.fileName = i_fileName;
  }
  public void run()
  {
     try
     {
       File f = new File(this.fileName);
       fileLength = f.length();
       byte[] b = new byte[fileLength];
       FileInputStream f = new FileInputStream(f);
       f.read(b);
       /* pls also note that since u are reading the
          file just at once this will further
          add to the efficiency
       */
        /* pass the byte back to the calling func and add
           it to the zip file
        */
     }
     catch(Exception e)
     {
       // do whatever.
     }
  }
  private fileName;
}


After this in ur loop whenever u get a file and not directory for reading and that file use thread like:

Thread t = new readThisFile("filename.txt");
t.start();

and this will start a new thread for every file u read.


I hope this will help u.

-Pandey
Pandey and sghosh:

You two have been a great help to me.
Although I managed to  program to work by myself but still you showed me the way how to go about it.

Anyways points go to sghosh because I think that he spent his time in writing down the codes for me.

Jimmy
I can spend 200 pts. on this one.
I will accept this one for sghosh and post a 100 pt. question for u also Pandey....

If sghosh doesnt have any problem??

Jimmy
ASKER CERTIFIED SOLUTION
Avatar of sghosh092199
sghosh092199

Link to home
membership
This solution is only available to members.
To access this solution, you must be a member of Experts Exchange.
Start Free Trial
Jimmy,
   Whatever u want u can do. I don't have any problem.
   
Anyway this(team work) is a very nice way to learn things.
all the best.
-pandey