/* * Copyright 2012 jMethods, Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.myjavaworld.zip; import java.io.BufferedInputStream; import java.io.File; import java.io.FileInputStream; import java.io.FileOutputStream; import java.io.IOException; import java.util.zip.ZipEntry; import java.util.zip.ZipOutputStream; import javax.swing.event.EventListenerList; import com.myjavaworld.jftp.LocalFile; import com.myjavaworld.util.Filter; import com.myjavaworld.util.ProgressEvent; import com.myjavaworld.util.ProgressListener; /** * This class is used to create ZIP files. * * @author Sai Pullabhotla */ public class Zip { /** * target ZIP file. */ private File file = null; /** * Directory, relative to which the entries will be added to this zip file. */ private String relativeTo = null; /** * Output stream to write to this zip file. */ private ZipOutputStream zout = null; /** * Event listener list */ private EventListenerList listenerList = null; private Filter filter = null; /** * Creates an instance of <code>ZipFile</code>. * * @param file * compressed file to create */ public Zip(File file) { this.file = file; listenerList = new EventListenerList(); } public File getFile() { return file; } public void setFilter(Filter filter) { this.filter = filter; } public Filter getFilter() { return filter; } public void addZipListener(ZipListener l) { listenerList.add(ZipListener.class, l); } public void removeZipListener(ZipListener l) { listenerList.remove(ZipListener.class, l); } public void addProgressListener(ProgressListener l) { listenerList.add(ProgressListener.class, l); } public void removeProgressListener(ProgressListener l) { listenerList.remove(ProgressListener.class, l); } protected void fireBeginFileEvent(File file) { Object[] listeners = listenerList.getListenerList(); for (int i = listeners.length - 2; i >= 0; i -= 2) { if (listeners[i] == ZipListener.class) { ((ZipListener) listeners[i + 1]).beginFile(new ZipEvent(this, ZipEvent.ZIP, file.getName())); } } } protected void fireEndFileEvent(File file) { Object[] listeners = listenerList.getListenerList(); for (int i = listeners.length - 2; i >= 0; i -= 2) { if (listeners[i] == ZipListener.class) { ((ZipListener) listeners[i + 1]).endFile(new ZipEvent(this, ZipEvent.ZIP, file.getName())); } } } protected void fireProgressEvent(int progress) { Object[] listeners = listenerList.getListenerList(); for (int i = listeners.length - 2; i >= 0; i -= 2) { if (listeners[i] == ProgressListener.class) { ((ProgressListener) listeners[i + 1]) .progressChanged(new ProgressEvent(this, progress)); } } } /** * Returns the directory, relative to which the entries will be added to * this zip file. * * @return relative directory. */ public File getRelativeTo() { return new File(relativeTo); } /** * Sets the directory, relative to which all entries will be added to the * zip file. * * @param relativeTo * directory, relative to which the entries will be added. */ public void setRelativeTo(File relativeTo) { try { this.relativeTo = relativeTo.getCanonicalPath(); } catch (IOException exp) { this.relativeTo = relativeTo.getAbsolutePath(); } } /** * Opens this <code>ZipFile</code> for writing. This method must be called * before adding any entries to this <code>CompressedFile</code>. * * @throws IOException * propogated. */ public void open() throws IOException { zout = new ZipOutputStream(new FileOutputStream(file)); } /** * Closes this <code>ZipFile</code>. No more entries can be added to this * <code>ZipFile</code> after calling this method. An attempt to add more * entries after calling this method will result in an * <code>IllegalStateException</code>. * * @throws IOException * propogated. */ public void close() throws IOException { if (zout != null) { zout.close(); } zout = null; } /** * Adds a new entry to this <code>ZipFile</code>. * * @param file * @throws IOException */ public void addEntry(File file) throws IOException { if (zout == null) { throw new IllegalStateException( "Zip File was never opened or is already closed. "); } // Let's not try to add the file that we are creating to itself. if (file.equals(this.file)) { return; } ZipEntry entry = new ZipEntry(computeEntryName(file)); entry.setTime(file.lastModified()); zout.putNextEntry(entry); if (file.isFile()) { addFile(file); zout.closeEntry(); } else if (file.isDirectory()) { zout.closeEntry(); LocalFile[] children = new LocalFile(file).list(filter); if (children != null) { for (int i = 0; i < children.length; i++) { addEntry(children[i].getFile()); } } } } /** * Adds the given data file to this <code>ZipFile</code>. * * @param file * data file to add * @throws IOException * propogated. */ private void addFile(File file) throws IOException { fireBeginFileEvent(file); final int bufferSize = 16 * 1024; byte[] buffer = new byte[bufferSize]; int bytesRead = 0; final long fileSize = file.length(); long totalBytesRead = 0L; BufferedInputStream bin = null; try { bin = new BufferedInputStream(new FileInputStream(file)); while ((bytesRead = bin.read(buffer)) != -1) { zout.write(buffer, 0, bytesRead); totalBytesRead += bytesRead; int progress = (int) ((totalBytesRead * 100) / fileSize); fireProgressEvent(progress); } } finally { fireEndFileEvent(file); if (bin != null) { bin.close(); } } } /** * Computes and returns the enry name for the given file. * * @param file * file to add. * @return entry name */ private String computeEntryName(File file) { String entryName = null; String path = null; try { path = file.getCanonicalPath(); } catch (IOException exp) { path = file.getAbsolutePath(); } if (relativeTo == null) { entryName = path; } else { entryName = path.substring(relativeTo.length()); } if (entryName.startsWith(File.separator)) { entryName = entryName.substring(1); } if (File.separatorChar != '/') { entryName = entryName.replace(File.separatorChar, '/'); } if (file.isDirectory()) { entryName += "/"; } return entryName; } }