随笔 - 117  文章 - 72  trackbacks - 0

声明:原创作品(标有[原]字样)转载时请注明出处,谢谢。

常用链接

常用设置
常用软件
常用命令
 

订阅

订阅

留言簿(7)

随笔分类(130)

随笔档案(123)

搜索

  •  

积分与排名

  • 积分 - 152896
  • 排名 - 390

最新评论

[关键词]:ant,zip,unzip,Apache,压缩,解压,中文乱码,ZipEntry
     先前写了一篇blog《使用org.apache.tools.zip实现zip压缩和解压》,现对它进行了改进:找出了几个Bug,修改了部分代码,增加了注释,添加了图形界面,打了个可执行包,双就可以运行了。源代码如下,希望大家多提意见。

MyZip.java:
package myzip;
import java.io.*;
import javax.swing.*;
import java.awt.*;
import java.awt.event.*;
import myzip.AntZip;

/**
 *界面类,调用AntZip类实现压缩解压功能。
 *@version 2009-3-18
 *@author Winty (wintys@gmail.com)
 */
public class MyZip{
    public static void main(String[] args){
        new MyZip(new AntZip());
    }

    public MyZip(AntZip zip){
        this.zip = zip;
        this.latestDir = new File(".");
        buildUI();       
    }

    public void buildUI(){
        jframe = new JFrame();
        jframe.setTitle("MyZip");
        jframe.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        jframe.setResizable(false);
        jframe.setSize(230 , 150);
        Dimension screen= Toolkit.getDefaultToolkit().getScreenSize();
        jframe.setLocation((screen.width-jframe.getWidth())/2,
                                     (screen.height-jframe.getWidth())/2);

        jframe.setLayout(null);
        Container contentPane = jframe.getContentPane();

        JButton zipBtn;
        JButton unzipBtn;
        zipBtn = new JButton("压缩");
        unzipBtn = new JButton("解压");
        zipBtn.setSize(60 , 40);
        unzipBtn.setSize(60 , 40);
        zipBtn.setLocation(40,40);
        unzipBtn.setLocation(120 , 40);
        contentPane.add(zipBtn);
        contentPane.add(unzipBtn);

        zipBtn.addActionListener(new ActionHandler());
        unzipBtn.addActionListener(new ActionHandler());

        jframe.setVisible(true);
    }

    private JFrame jframe;
    private AntZip zip;
    private File latestDir;/*记录最近使用的文件夹路径*/

    /*内部类监听器*/
    class ActionHandler implements ActionListener{
        public void actionPerformed(ActionEvent event){
            JFileChooser chooser = new JFileChooser();
            chooser.setCurrentDirectory(latestDir);

            String cmd = event.getActionCommand();
            if(cmd.equals("压缩")){
                chooser.setFileSelectionMode(
                    JFileChooser.FILES_AND_DIRECTORIES);
                chooser.setMultiSelectionEnabled(true);

                int returnVal = chooser.showOpenDialog(jframe);
                if(returnVal == JFileChooser.APPROVE_OPTION) {
                    File[] files = chooser.getSelectedFiles();
                    if(files!=null){
                        zip.doZip(files , files[0].getName());
                        latestDir = files[0].getParentFile();
                    }
                }
            }
            else if(cmd.equals("解压")){
                chooser.setMultiSelectionEnabled(false) ;

                int returnVal = chooser.showOpenDialog(jframe);
                if(returnVal == JFileChooser.APPROVE_OPTION) {
                    File file = chooser.getSelectedFile();
                    if(file!=null){
                        zip.unZip(file);
                        latestDir = file.getParentFile();
                    }
                }
            }

        }
    }
}

AntZip.java:
package myzip;
import java.io.*;
import org.apache.tools.zip.*;
import java.util.Enumeration;
/**
*功能:zip压缩、解压(支持中文文件名)
*说明:使用Apache Ant提供的zip工具org.apache.tools.zip实现zip压缩和解压功能.
*   解决了由于java.util.zip包不支持汉字的问题。
*   使用java.util.zip包时,当zip文件中有名字为中文的文件时,
*   就会出现异常:
*        "Exception  in thread "main " java.lang.IllegalArgumentException
*      at   java.util.zip.ZipInputStream.getUTF8String(ZipInputStream.java:285)
*
*注意:
*   1、使用时把ant.jar放到classpath中,程序中使用import org.apache.tools.zip.*;
*   2、Apache Ant 下载地址:http://ant.apache.org/
*   3、Ant ZIP Online API:
*www.jajakarta.org/ant/ant-1.6.1/docs/mix/manual/api/org/apache/tools/zip/
*   4、本程序使用Ant 1.7.1 中的ant.jar。
*   5、如果只需要Ant的zip压缩功能,不需要Ant的其它功能,
*     那么可以减小ant.jar的大小。方法是用WinRAR打开ant.jar,
*     把没有用到的包和class文件都删除。这样ant.jar体积就减小了。
*   6、ZipEntry的isDirectory()方法中,目录以"/"结尾。
*
*仅供编程学习参考.
*
*Copyright (c) Winty
*http://www.blogjava.net/wintys
*
*@author Winty (wintys@gmail.com)
*@version   2008-8-3
*------------------------------------------------
*可将主函数注释去掉以单独测试AntZip类。
*Compile:
*   javac -cp Ant.jar AntZip.java
*
*Usage:(将ant.jar直接放在当前目录)
*   压缩:java -cp Ant.jar;.  AntZip -zip [directoryName | fileName]...
*   解压:java -cp Ant.jar;.  AntZip -unzip "fileName.zip"
*
*------------------------------------------------
*2009-3-17:
*修正一处Bug,当解压的zip文件中根目录下直接有文件时会出错。
*将unZip()中的if(!parent.exists())改正为:if(parent!=null && !parent.exists())
*
*2009-3-18:
*多处其它修改
*------------------------------------------------
*/

public class AntZip{
    private ZipFile         zipFile;
    private ZipOutputStream zipOut;     //压缩Zip
    private ZipEntry        zipEntry;
    private static int      bufSize;    //size of bytes
    private byte[]          buf;
    private int             readedBytes;
    //用于压缩中。要去除的绝对父路路径,目的是将绝对路径变成相对路径。
    private String deleteAbsoluteParent;
   
    /**
     *构造方法。默认缓冲区大小为512字节。
     */
    public AntZip(){
        this(512);
    }

    /**
     *构造方法。
     *@param bufSize 指定压缩或解压时的缓冲区大小
     */
    public AntZip(int bufSize){
        this.bufSize = bufSize;
        this.buf = new byte[this.bufSize];
        deleteAbsoluteParent = null;
    }
   
    /**
     *压缩文件夹内的所有文件和目录。
     *@param zipDirectory 需要压缩的文件夹名
     */
    public void doZip(String zipDirectory){
        File zipDir = new File(zipDirectory);
        doZip(new File[]{zipDir} , zipDir.getName());
    }

    /**
     *压缩多个文件或目录。可以指定多个单独的文件或目录。而
     *<code>doZip(String zipDirectory)</code>则直接压缩整个文件夹。
     *@param files 要压缩的文件或目录组成的<code>File</code>数组。
     *@param zipFileName 压缩后的zip文件名,如果后缀不是".zip",
     *  自动添加后缀".zip"。
     */
    public void doZip(File[] files , String zipFileName){
        //未指定压缩文件名,默认为"ZipFile"
        if(zipFileName==null || zipFileName.equals(""))
            zipFileName = "ZipFile";

        //添加".zip"后缀
        if(!zipFileName.endsWith(".zip"))
            zipFileName += ".zip";

        try{
            this.zipOut = new ZipOutputStream(
                new BufferedOutputStream(new FileOutputStream(zipFileName)));
            compressFiles(files , this.zipOut , true );
            this.zipOut.close();
        }catch(IOException ioe){
            ioe.printStackTrace();
        }
    }

    /**
     *压缩文件和目录。由doZip()调用
     *@param files 要压缩的文件
     *@param zipOut zip输出流
     *@param isAbsolute 是否是要去除的绝对路径的根路径。因为compressFiles()
     *会递归地被调用,所以只用deleteAbsoluteParent不行。必须用isAbsolute来指明
     *compressFiles()是第一次调用,而不是后续的递归调用。即如果要压缩的路径是
     *E:\temp,那么第一次调用时,isAbsolute=true,则deleteAbsoluteParent会记录
     *要删除的路径就是E:\ ,当压缩子目录E:\temp\folder时,isAbsolute=false,
     *再递归调用compressFiles()时,deleteAbsoluteParent仍然是E:\ 。从而保证了
     *将E:\temp及其子目录均正确地转化为相对目录。这样压缩才不会出错。不然绝对
     *路径E:\也会被写入到压缩文件中去。
     */
    private void compressFiles(File[] files ,
                                            ZipOutputStream zipOut ,
                                            boolean isAbsolute) throws IOException{

        for(File file : files){
            if(file==null)continue; //空的文件对象

            //删除绝对父路径
            if(file.isAbsolute()){
                if(isAbsolute){
                    deleteAbsoluteParent = file.getParentFile().getAbsolutePath();
                    deleteAbsoluteParent = appendSeparator(deleteAbsoluteParent);
                }
            }
            else
                deleteAbsoluteParent = "";

            if(file.isDirectory()){//是目录
                compressFolder(file , zipOut);
            }
            else{//是文件
                compressFile(file , zipOut);
            }
        }
    }

    /**
     *压缩文件或空目录。由compressFiles()调用。
     *@param file 需要压缩的文件
     *@param zipOut zip输出流
     */
    public void compressFile(File file , ZipOutputStream zipOut)
        throws IOException{

        String fileName = file.toString();

        /*去除绝对父路径。*/
        if(file.isAbsolute())
            fileName = fileName.substring(deleteAbsoluteParent.length());
        if(fileName == null || fileName=="")
            return;


        /*因为是空目录,所以要在结尾加一个"/"。
          不然就会被当作是空文件。
          ZipEntry的isDirectory()方法中,目录以"/"结尾.
          org.apache.tools.zip.ZipEntry :
            public boolean isDirectory() {
                return getName().endsWith("/");
            }
          */
        if(file.isDirectory())
            fileName = fileName + "/";//此处不能用"\\"

        zipOut.putNextEntry(new ZipEntry(fileName));

        //如果是文件则需读;如果是空目录则无需读,直接转到zipOut.closeEntry()。
        if(file.isFile()){
            FileInputStream fileIn = new FileInputStream(file);
            while((this.readedBytes = fileIn.read(this.buf))>0){
                zipOut.write(this.buf , 0 , this.readedBytes);
            }
            fileIn.close();
        }

        zipOut.closeEntry();
    }

    /**
     *递归完成目录文件读取。由compressFiles()调用。
     *@param dir 需要处理的文件对象
     *@param zipOut zip输出流
     */
    private void compressFolder(File dir , ZipOutputStream zipOut)
        throws IOException{
       
        File[] files = dir.listFiles();
   
        if(files.length == 0)//如果目录为空,则单独压缩空目录。
            compressFile(dir , zipOut);
        else//如果目录不为空,则分别处理目录和文件.
            compressFiles(files , zipOut , false);
    }
   
    /**
     *解压指定zip文件。
     *@param unZipFileName 需要解压的zip文件名
     */
    public void unZip(String unZipFileName){
        FileOutputStream fileOut;
        File file;
        InputStream inputStream;

        try{
            this.zipFile = new ZipFile(unZipFileName);

            for(Enumeration entries = this.zipFile.getEntries();
                entries.hasMoreElements(); ){

                ZipEntry entry = (ZipEntry)entries.nextElement();
                file = new File(entry.getName());

                if(entry.isDirectory()){//是目录,则创建之
                    file.mkdirs();
                }
                else{//是文件
                    //如果指定文件的父目录不存在,则创建之.
                    File parent = file.getParentFile();
                    if(parent!=null && !parent.exists()){
                        parent.mkdirs();
                    }

                    inputStream = zipFile.getInputStream(entry);

                    fileOut = new FileOutputStream(file);
                    while(( this.readedBytes = inputStream.read(this.buf) ) > 0){
                        fileOut.write(this.buf , 0 , this.readedBytes );
                    }
                    fileOut.close();

                    inputStream.close();
                }  
            }
            this.zipFile.close();
        }catch(IOException ioe){
            ioe.printStackTrace();
        }
    }

   
    /**
     *给文件路径或目录结尾添加File.separator
     *@param fileName 需要添加路径分割符的路径
     *@return 如果路径已经有分割符,则原样返回,否则添加分割符后返回。
     */
    private String appendSeparator(String path){
        if(!path.endsWith(File.separator))
                        path += File.separator;
        return path;
    }

    /**
     *解压指定zip文件。
     *@param unZipFile 需要解压的zip文件对象
     */
    public void unZip(File unZipFile){
        unZip(unZipFile.toString());
    }

    /**
     *设置压缩或解压时缓冲区大小。
     *@param bufSize 缓冲区大小
     */
    public void setBufSize(int bufSize){
        this.bufSize = bufSize;
    }

    //主函数,用于测试AntZip类
    /*
    public static void main(String[] args)throws Exception{
        if(args.length>=2){
            AntZip zip = new AntZip();

            if(args[0].equals("-zip")){
                //将后续参数全部转化为File对象
                File[] files = new File[ args.length - 1];
                for(int i = 0;i < args.length - 1; i++){
                    files[i] = new File(args[i + 1]);
                }

                //将第一个文件名作为zip文件名
                zip.doZip(files , files[0].getName());

                return ;
            }
            else if(args[0].equals("-unzip")){
                zip.unZip(args[1]);
                return ;
            }
        }
       
        System.out.println("Usage:");
        System.out.println("压缩:java AntZip -zip [directoryName | fileName]... ");
        System.out.println("解压:java AntZip -unzip fileName.zip");
    }
    */
}

附件内容:
/MyZip.jar : 打包后的可执行文件,可单独运行,内含org.apache.tools.zip包。运行环境JRE1.6+
/使用Ant实现zip压缩解压功能/ant.jar : 精简后的ant包,只包含org.apache.tools.zip
/使用Ant实现zip压缩解压功能/AntZip.java
/使用Ant实现zip压缩解压功能/MyZip.java

[附件]:使用Ant实现zip压缩解压功能.zip
posted on 2009-03-19 13:17 天堂露珠 阅读(4743) 评论(11)  编辑  收藏 所属分类: Java

FeedBack:
# re: [原]使用Ant实现zip压缩解压功能 2009-06-11 16:23 猪影横飞
你好,学习了你的ANT实现ZIP压缩技术,受益匪浅,想问下,如果我想做成压缩双机文件通信(即采用服务端/客户端形式),点击发送端按钮->选择文件->压缩成*.zip文件->发送给客户端,点击接收端按钮->选择保存路径->接收*.zip文件->解压*.zip文件,请问如何实现和调用?
hszcy521@163.com  回复  更多评论
  
# re: [原]使用Ant实现zip压缩解压功能 2009-06-11 20:17 天堂露珠
那就需要用到TCP或UDP通信了,参见我的CSDN文章:《Java UDP网络编程 - 最简单示例》:http://blog.csdn.net/wintys/archive/2008/12/15/3525643.aspx ,以及《Java TCP网络编程 - 最简单示例》:http://blog.csdn.net/wintys/archive/2008/12/15/3525619.aspx 。  回复  更多评论
  
# re: [原]使用Ant实现zip压缩解压功能 2009-06-11 21:56 猪影横飞
恩,这个我会写已经写好了SERVER.JAVA和CLIENT.JAVA了,但里边都带有main函数,在JDK编程的环境下能直接运行,我想问的就是如何再GUI.JAVA里的事件处理中按我的流程调用?服务端先压缩再传送,客户端先接收再解压  回复  更多评论
  
# re: [原]使用Ant实现zip压缩解压功能 2009-06-11 23:16 天堂露珠
@猪影横飞
两个客户端都一直在监听着指定的端口,用线程实现监听。
以下仅为示意性代码:

1、发送
比如就用我写的MyZip.java的界面,把"压缩"改成"发送",
if(cmd.equals("压缩")){
......
if(files!=null){
//先压缩所选文件夹内的文件,结果会生成一个zip文件,假设为"file.zip"
zip.doZip(files , files[0].getName());
latestDir = files[0].getParentFile();

//udp为UDP通信类。
//获取"file.zip"的字节数组,启动一个线程,
//把它当作一个或多个数据包发送出去,
//而此时,另一个客户端正在监听着,它收到数据后,进行相反的操作。
udp.send(filePacket);
}
......
}


2、接收
也就是说,没有数据发送的时候,两个客户端都有一个后台线程运行着:
class Client implements Runnable{
public void run(){
while(true){
//接收,没有收到时,将一直等待
datagramSocket.receive(recvPacket);
//解压
Unzip();
}
}
}  回复  更多评论
  
# re: [原]使用Ant实现zip压缩解压功能 2009-06-12 22:36 猪影横飞
@天堂露珠
谢谢你的指点,非常感谢!~  回复  更多评论
  
# re: [原]使用Ant实现zip压缩解压功能 2009-06-13 21:07 王琦均
如果是要求以流的形式传入zip文件,那怎么解决呢
我看了ant里面好像没有提供这样的方法。
里面 没有zipInputStream 只有zipOutputStream

我这里提的方法就是:
//这里的流就是出入的zip文件流
public int getText(InputStream is){

}  回复  更多评论
  
# re: [原]使用Ant实现zip压缩解压功能 2009-06-13 22:34 天堂露珠
@王琦均
只有自己独立解决问题,才可能真正成长,不能每一步都问吧。
其实我的程序中已经用到了,org.apache.tools.zip.ZipFile.getInputStream(ZipEntry ze)就是输入流。  回复  更多评论
  
# re: [原]使用Ant实现zip压缩解压功能 2009-06-14 01:20 王琦均
@天堂露珠
外部只调我这个程序的这个方法哦
也就是外部程序只给我传个流进来。  回复  更多评论
  
# re: [原]使用Ant实现zip压缩解压功能 2009-06-14 01:23 王琦均
@天堂露珠
外部是不会用org.apache.tools.zip.ZipFile.getInputStream(ZipEntry ze)
来给你转化的。
直接就是个文件流传进来的!  回复  更多评论
  
# re: [原]使用Ant实现zip压缩解压功能 2009-06-29 15:41 shappy
@王琦均 可能我的答案是你想要的,你是希望直接输出zip流,而不是zip文件吗
//创建zip流
ByteArrayOutputStream out = new ByteArrayOutputStream();
ZipOutputStream zout = new ZipOutputStream(out);
//省略
ByteArrayInputStream zin= new ByteArrayInputStream(out.toByteArray());
byte[] aryRead = new byte[zin.available()];
//有inputstream,也有byte[]

  回复  更多评论
  
# re: [原]使用Ant实现zip压缩解压功能 2009-06-29 15:51 shappy
//创建zip流
ByteArrayOutputStream out = new ByteArrayOutputStream();
ZipOutputStream zout = new ZipOutputStream(out);
//省略压缩代码
zout.close(); //很奇怪,这里如果不关闭的话,检测到输出的流稍小一点,而且数据也有一些差异,谁能解释一下
ByteArrayInputStream zin= new ByteArrayInputStream(out.toByteArray());
byte[] aryRead = new byte[zin.available()];
//有inputstream,也有byte[]   回复  更多评论
  

只有注册用户登录后才能发表评论。


网站导航: