1. 测试程序最终截图
2. 定义组件支持的算法
package org.file.util.archive;
/**
* @author ALLEN
* <br/>Define the compress algorithm
* <br/>Support <b>{ Compress, UnCompress }</b>
*/
public enum Algorithm {
Compress, /* Compress Algorithm */
UnCompress /* UnCompress Algorithm */
}
3. 定义组件抽象父类
package org.file.util.archive;
import java.io.File;
import java.util.LinkedList;
import java.util.List;
import java.util.Properties;
/**
* @author ALLEN
* <br/>Define the parent case collection of Compress and UnCompress
*/
public abstract class Archive {
/**
* Define the default compress level { 9 }
*/
public static final int DEFAULT_COMPRESS_LEVEL = 9;
/**
* Define the default buffer size { 2 << 19 = 1MB }
*/
protected static final int DEFAULT_BUFFER_SIZE = 2 << 19;
/**
* Define the default file block { 100 }
*/
protected static final int DEFAULT_FILE_BLOCK = 100;
/**
* Define the archive algorithm
*/
protected Algorithm algorithm;
/**
* Define the ZIP file instance
*/
protected File zipFile;
/**
* Define the meta files instance
*/
protected File[] metaFiles;
/**
* Define the bytes which has finished reading
*/
protected long finished = 0L;
/**
* Define the current total compressed size
*/
protected long compressSize = 0L;
/**
* Define the total bytes of (sub)file(s)
*/
protected long total = 0L;
/**
* Define the task startup time
*/
protected long start = System.currentTimeMillis();
/**
* Define the task progress current time
*/
protected long current = this.start;
/**
* Define the properties instance
*/
protected final Properties properties = new Properties();
/**
* Define the progress listener linked list collection
*/
protected final List<ProgressListener> listeners = new LinkedList<ProgressListener>();
/**
* The default constructor
* @param algorithm
* the archive algorithm
* @param zipFile
* the ZIP file instance
* @param metaFiles
* the meta files array
*/
protected Archive(Algorithm algorithm, File zipFile, File... metaFiles) {
if (algorithm != null && zipFile != null) {
this.algorithm = algorithm;
this.zipFile = zipFile;
this.metaFiles = metaFiles;
this.properties.clear();
this.listeners.clear();
this.init();
}
}
/**
* Initialize the compress or uncompress components
*/
protected abstract void init();
/**
* The core compress and uncompress process
*/
protected abstract void process();
/**
* Notify all registered progress listeners
*/
protected void notifyListeners() {
for (ProgressListener listener : this.listeners)
listener.progress(this.finished, this.compressSize, this.total, this.start, this.current, this.properties);
}
/**
* Register a new progress listener into the listeners linked list
* @param listener
* the ProgressListener instance
*/
public void registerProgressListener(ProgressListener listener) {
if (listener != null)
synchronized (this.listeners) {
this.listeners.add(listener);
}
}
/**
* Remove a progress listener from the listeners linked list
* @param listener
* the ProgressListener instance
*/
public void removeProgressListener(ProgressListener listener) {
if (listener != null)
synchronized (this.listeners) {
this.listeners.remove(listener);
}
}
/**
* Start the process task to execute
*/
public void execute() {
synchronized (this) {
this.process();
}
}
}
4. 压缩文件或目录实现
package org.file.util.archive;
import java.io.BufferedInputStream;
import java.io.BufferedOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
import java.util.zip.ZipEntry;
import java.util.zip.ZipFile;
import java.util.zip.ZipOutputStream;
/**
* @author ALLEN
* <br/>The child Compress implement from archive
*/
public class Compress extends Archive {
/**
* Define the file collection vector
*/
private Map<String, File> hashMap;
/**
* Define the compress level
*/
private int level = DEFAULT_COMPRESS_LEVEL;
/**
* Define the compress comment
*/
private String comment = null;
/**
* The default constructor
* @param algorithm
* the archive algorithm
* @param zipFile
* the ZIP file instance
* @param metaFiles
* the meta files array
*/
protected Compress(Algorithm algorithm, File zipFile, File... metaFiles) {
super(algorithm, zipFile, metaFiles);
}
/**
* The recursive method which scans the file instance
*/
private void scan() {
for (File file : this.metaFiles) {
if (file.isFile()) {
this.total += file.length();
this.hashMap.put(file.getName(), file);
}
else if (file.isDirectory())
this.make(file, file.getAbsolutePath().length() + 1);
}
}
/**
* The recursive method which make new file record
* @param file
* the destination directory instance
* @param cutPos
* the parent directory cut position of current file
*/
private void make(File file, int cutPos) {
for (File tmp : file.listFiles()) {
if (tmp.isFile()) {
this.total += tmp.length();
this.hashMap.put(tmp.getAbsolutePath().substring(cutPos), tmp);
} else if (tmp.isDirectory())
this.make(tmp, cutPos);
}
}
/**
* Set the compress level and comment
* @param level
* the compress level
* @param comment
* the compress comment
*/
public void setLevelAndComment(int level, String comment) {
if (level > 0 && level < 10)
this.level = level;
this.comment = comment;
}
/* (non-Javadoc)
* @see org.file.util.archive.Archive#init()
*/
protected void init() {
this.hashMap = new HashMap<String, File>();
this.hashMap.clear();
this.scan();
}
/* (non-Javadoc)
* @see org.file.util.archive.Archive#process()
*/
protected void process() {
try {
// update compressed size
this.compressSize = this.total;
// create output stream instance
ZipOutputStream output = new ZipOutputStream(new BufferedOutputStream(new FileOutputStream(this.zipFile, false)));
output.setLevel(this.level);
output.setComment(this.comment);
// define the input parameters
BufferedInputStream input = null;
long fileLength = 0L;
int bufferSize = 0;
int length = -1;
byte[] buffer = null;
// parser files
Iterator<String> it = this.hashMap.keySet().iterator();
if (it != null) {
this.properties.setProperty("Count", String.valueOf(this.hashMap.size()));
this.start = System.currentTimeMillis();
while (it.hasNext()) {
// get file information
String relativePath = it.next();
File file = this.hashMap.get(relativePath);
// assign buffer size
if ((fileLength = file.length()) < 0)
continue;
if (fileLength < DEFAULT_FILE_BLOCK)
bufferSize = (int)fileLength;
else if (Math.round((double)fileLength / DEFAULT_FILE_BLOCK) < DEFAULT_BUFFER_SIZE)
bufferSize = DEFAULT_BUFFER_SIZE;
else
bufferSize = (int)Math.round((double)fileLength / DEFAULT_FILE_BLOCK);
buffer = new byte[bufferSize];
// create new file entry
output.putNextEntry(new ZipEntry(relativePath));
input = new BufferedInputStream(new FileInputStream(file), bufferSize);
while ((length = input.read(buffer, 0, bufferSize)) != -1) {
// update finished value
this.finished += length;
output.write(buffer, 0, length);
// update current time value
this.current = System.currentTimeMillis();
// notify progress listeners
this.notifyListeners();
}
input.close();
}
}
output.flush();
output.close();
// update compressed size
ZipFile zf = new ZipFile(this.zipFile.getAbsolutePath());
this.compressSize = 0L;
Iterator<String> _it = this.hashMap.keySet().iterator();
while (_it.hasNext())
this.compressSize += zf.getEntry(_it.next()).getCompressedSize();
zf.close();
this.notifyListeners();
} catch (Exception e) {
;
}
}
}
5. 解压缩文件实现
package org.file.util.archive;
import java.io.BufferedOutputStream;
import java.io.File;
import java.io.FileOutputStream;
import java.io.InputStream;
import java.io.OutputStream;
import java.util.Enumeration;
import java.util.Iterator;
import java.util.Vector;
import java.util.zip.ZipEntry;
import java.util.zip.ZipFile;
/**
* @author ALLEN
* <br/>The child UnCompress implement from archive
*/
public class UnCompress extends Archive {
/**
* Define the file entry collection
*/
private Vector<String> vector;
/**
* The default constructor
* @param algorithm
* the archive algorithm
* @param zipFile
* the ZIP file instance
* @param metaFile
* the meta directory
*/
protected UnCompress(Algorithm algorithm, File zipFile, File metaFile) {
super(algorithm, zipFile, metaFile);
}
/**
* Scan the ZIP compress file entries
*/
private void scan() {
try {
ZipFile zf = new ZipFile(this.zipFile.getAbsolutePath());
Enumeration<?> en = zf.entries();
if (en != null) {
while (en.hasMoreElements())
this.vector.add(((ZipEntry)en.nextElement()).getName());
}
zf.close();
this.properties.setProperty("Count", String.valueOf(zf.size()));
} catch (Exception e) {
;
}
}
/* (non-Javadoc)
* @see org.file.util.archive.Archive#init()
*/
protected void init() {
this.vector = new Vector<String>();
this.vector.clear();
this.scan();
}
/* (non-Javadoc)
* @see org.file.util.archive.Archive#process()
*/
protected void process() {
File path = null;
if (this.metaFiles.length != 1 || !(path = this.metaFiles[0]).isDirectory())
return;
try {
// create ZIP file instance
ZipFile zf = new ZipFile(this.zipFile);
Iterator<String> _it = this.vector.iterator();
while (_it.hasNext()) {
ZipEntry entry = zf.getEntry(_it.next());
this.compressSize += entry.getCompressedSize();
this.total += entry.getSize();
}
// define uncompress parameters
InputStream input = null;
String absolutePath = null;
File tmp = null;
OutputStream output = null;
long entryLength = 0L;
int bufferSize = 0;
int length = -1;
byte[] buffer;
// create iterator
Iterator<String> it = this.vector.iterator();
if (it != null) {
this.start = System.currentTimeMillis();
while (it.hasNext()) {
// get file entry relative path
String relativePath = it.next();
ZipEntry entry = zf.getEntry(relativePath);
// assign buffer size
if ((entryLength = entry.getCompressedSize()) < 0)
continue;
if (entryLength < DEFAULT_FILE_BLOCK)
bufferSize = (int)entryLength;
else if (Math.round((double)entryLength / DEFAULT_FILE_BLOCK) < DEFAULT_BUFFER_SIZE)
bufferSize = DEFAULT_BUFFER_SIZE;
else
bufferSize = (int)Math.round((double)entryLength / DEFAULT_FILE_BLOCK);
buffer = new byte[bufferSize];
// get input stream instance
input = zf.getInputStream(entry);
// create file is not exist
absolutePath = path.getAbsolutePath() + System.getProperty("file.separator") + relativePath;
tmp = new File(absolutePath.substring(0, absolutePath.lastIndexOf(System.getProperty("file.separator"))));
tmp.mkdirs();
tmp = new File(absolutePath);
tmp.createNewFile();
// uncompress data
output = new BufferedOutputStream(new FileOutputStream(tmp, false), bufferSize);
while ((length = input.read(buffer, 0, bufferSize)) != -1) {
// update finished value
this.finished += length;
output.write(buffer, 0, length);
// update current time value
this.current = System.currentTimeMillis();
// notify progress listeners
this.notifyListeners();
}
output.flush();
output.close();
input.close();
}
}
zf.close();
} catch (Exception e) {
e.printStackTrace();
}
}
}
6. 定义回掉函数,作为组件监听器
package org.file.util.archive;
import java.util.Properties;
/**
* @author ALLEN
* <br/>Define the archive progress listener
*/
public interface ProgressListener {
/**
* Define the progress listener method which can read the archive progress
* @param finished
* the total bytes finished handling
* @param compressSize
* the current total compress size
* @param total
* the total bytes of data
* @param start
* the start time of progress
* @param current
* the current time of progress
* @param properties
* the result properties of archive
* <br/>properties key should be in <b>{ Count }<b/>
*/
public void progress(long finished, long compressSize, long total, long start, long current, Properties properties);
}
7. 组件核心控制器
package org.file.util.archive;
import java.io.File;
/**
* @author ALLEN
* <br/>Define the FileArchive which manages Compress and UnCompress
*/
public class FileArchive implements Runnable {
/**
* Define the archive instance
*/
private Archive archive;
/**
* The default constructor
* @param algorithm
* the archive algorithm
* @param zipFile
* the destination ZIP file
* @param metaFiles
* the meta files
*/
private FileArchive(Algorithm algorithm, File zipFile, File... metaFiles) {
if (algorithm == Algorithm.Compress) {
this.archive = new Compress(algorithm, zipFile, metaFiles);
} else if (algorithm == Algorithm.UnCompress) {
metaFiles[0].mkdirs();
this.archive = new UnCompress(algorithm, zipFile, metaFiles[0]);
}
}
/**
* Return an instance of FileArchive
* @param algorithm
* the archive algorithm
* @param absolutePath
* the destination ZIP file
* @param metaFiles
* the meta files
* @return
* a new instance of FileArchive
*/
public static FileArchive getInstance(Algorithm algorithm, String absolutePath, String... metaFiles) {
return getInstance(algorithm, new File(absolutePath), metaFiles);
}
/**
* Return an instance of FileArchive
* @param algorithm
* the archive algorithm
* @param zipFile
* the destination ZIP file
* @param metaFiles
* the meta files
* @return
* a new instance of FileArchive
*/
public static FileArchive getInstance(Algorithm algorithm, File zipFile, String... metaFiles) {
if (metaFiles != null) {
int length = metaFiles.length;
File[] files = new File[length];
for (int i = 0; i < length; i++)
files[i] = new File(metaFiles[i]);
return getInstance(algorithm, zipFile, files);
} else
return null;
}
/**
* Return an instance of FileArchive
* @param algorithm
* the archive algorithm
* @param absolutePath
* the destination ZIP file
* @param metaFiles
* the meta files
* @return
* a new instance of FileArchive
*/
public static FileArchive getInstance(Algorithm algorithm, String absolutePath, File... metaFiles) {
return getInstance(algorithm, new File(absolutePath), metaFiles);
}
/**
* Return an instance of FileArchive
* @param algorithm
* the archive algorithm
* @param zipFile
* the destination ZIP file
* @param metaFiles
* the meta files
* @return
* a new instance of FileArchive
*/
public static FileArchive getInstance(Algorithm algorithm, File zipFile, File... metaFiles) {
if (algorithm != null && zipFile != null && metaFiles != null)
return new FileArchive(algorithm, zipFile, metaFiles);
return null;
}
/**
* Register a new progress listener into the listeners linked list
* @param listener
* the ProgressListener instance
*/
public void registerProgressListener(ProgressListener listener) {
if (this.archive != null)
this.archive.registerProgressListener(listener);
}
/**
* Remove a progress listener from the listeners linked list
* @param listener
* the ProgressListener instance
*/
public void removeProgressListener(ProgressListener listener) {
if (this.archive != null)
this.archive.removeProgressListener(listener);
}
/**
* Start the archive task
*/
public synchronized void execute() {
new Thread(this).start();
}
/* (non-Javadoc)
* @see java.lang.Runnable#run()
*/
public void run() {
if (this.archive != null)
this.archive.execute();
}
}
8. 核心测试代码
package org.file.util.archive;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.Rectangle;
import java.awt.Toolkit;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.io.File;
import java.util.Properties;
import javax.swing.ButtonGroup;
import javax.swing.ImageIcon;
import javax.swing.JButton;
import javax.swing.JFileChooser;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.JProgressBar;
import javax.swing.JRadioButton;
import javax.swing.SwingUtilities;
import javax.swing.UIManager;
/**
* @author ALLEN
* <br/>Archive test case
*/
public class ArchiveTest extends JFrame implements ProgressListener {
private static final long serialVersionUID = -3707959572261992767L;
private static String path = null;
private static FileArchive fa;
private static ArchiveTest self;
private JLabel title;
private JProgressBar progress;
private JLabel label1;
private JLabel label2;
private JLabel label3;
private static ButtonGroup group;
private static JRadioButton button1;
private static JRadioButton button2;
private static JButton broswer;
private static JButton un_compress;
public ArchiveTest() {
super("JAVA SDK FILEARCHIVE");
self = this;
this.setIconImage(new ImageIcon(this.getClass().getResource("/org/data/resource/images/title.png")).getImage());
try {
UIManager.setLookAndFeel("com.sun.java.swing.plaf.nimbus.NimbusLookAndFeel");
} catch (Exception e) {
;
}
JPanel jPanel = new JPanel(null);
jPanel.setOpaque(true);
jPanel.setBackground(Color.ORANGE);
jPanel.setPreferredSize(new Dimension(330, 170));
this.title = new JLabel("*** JAVA SDK FILEARCHIVE 1.0.0.0 TESTCASE ***", JLabel.CENTER);
this.title.setBounds(new Rectangle(10, 3, 310, 25));
this.progress = new JProgressBar();
this.progress.setStringPainted(true);
this.progress.setValue(0);
this.progress.setString("Waiting for selecting ......");
this.progress.setBounds(new Rectangle(10, 25, 310, 25));
this.label1 = new JLabel("[ Size ] [ Time ]", JLabel.CENTER);
this.label1.setBounds(new Rectangle(10, 50, 310, 25));
this.label2 = new JLabel("[ Speed ] [ Remain ]", JLabel.CENTER);
this.label2.setBounds(new Rectangle(10, 70, 310, 25));
this.label3 = new JLabel("[ Count ] [ Compressed Ratio ]", JLabel.CENTER);
this.label3.setBounds(new Rectangle(10, 90, 310, 25));
group = new ButtonGroup();
button1 = new JRadioButton("Compress");
button1.setBounds(new Rectangle(60, 111, 100, 25));
button2 = new JRadioButton("UnCompress");
button2.setBounds(new Rectangle(180, 111, 100, 25));
broswer = new JButton("Broswer");
broswer.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
JFileChooser chooser = new JFileChooser();
chooser.setFileSelectionMode(JFileChooser.DIRECTORIES_ONLY);
int returnVal = chooser.showOpenDialog(null);
if(returnVal == JFileChooser.APPROVE_OPTION) {
path = chooser.getSelectedFile().getAbsolutePath();
}
}
});
broswer.setBounds(new Rectangle(10, 136, 150, 25));
un_compress = new JButton("(Un) Compress");
un_compress.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
if (path != null) {
if (group.getSelection() == button1.getModel()) {
fa = FileArchive.getInstance(Algorithm.Compress, new File("FILEARCHIVE.ZIP"), path);
fa.registerProgressListener(self);
broswer.setEnabled(false);
un_compress.setEnabled(false);
fa.execute();
} else if (group.getSelection() == button2.getModel()) {
fa = FileArchive.getInstance(Algorithm.UnCompress, new File("FILEARCHIVE.ZIP"), path);
fa.registerProgressListener(self);
broswer.setEnabled(false);
un_compress.setEnabled(false);
fa.execute();
}
}
}
});
un_compress.setBounds(new Rectangle(168, 136, 150, 25));
jPanel.add(this.title);
jPanel.add(this.progress);
jPanel.add(this.label1);
jPanel.add(this.label2);
jPanel.add(this.label3);
group.add(button1);
group.add(button2);
group.setSelected(button1.getModel(), true);
jPanel.add(button1);
jPanel.add(button2);
jPanel.add(broswer);
jPanel.add(un_compress);
this.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
this.setContentPane(jPanel);
this.setSize(new Dimension(330, 170));
this.setResizable(false);
this.setVisible(true);
Dimension resolution = Toolkit.getDefaultToolkit().getScreenSize();
this.setLocation((int)resolution.getWidth() / 2 - 165, (int)resolution.getHeight() / 2 - 133);
this.pack();
}
public void progress(long finished, long compressSize, long total, long start, long current, Properties properties) {
float percent = (float) finished * 100 / total;
long cost = current - start;
double speed = (double)finished * 0.9765625 / cost;
long remain = cost * (total - finished) / finished;
this.progress.setValue((int)percent);
this.progress.setString(String.format("%.2f%%", percent));
this.label1.setText(String.format("[ Size: %d Bytes ] [ Time: %d ms ]", total, cost));
this.label2.setText(String.format("[ Speed: %.2f Bps ] [ Remain: %d ms ]", speed, remain));
this.label3.setText(String.format("[ Count: %s ] [ Compressed Ratio: %.2f%% ]", properties.getProperty("Count"), (double)compressSize * 100 / total));
if (finished == total) {
broswer.setEnabled(true);
un_compress.setEnabled(true);
}
}
public static void main(String[] args) {
SwingUtilities.invokeLater(new Runnable() {
public void run() {
com.sun.awt.AWTUtilities.setWindowOpacity(new ArchiveTest(), 1.00f);
}
});
}
}