IT_Programming/Java

[펌] Java 자료 압축, 자료 손상 검사, 파일 묶기

JJun ™ 2010. 12. 30. 14:02

-------------------------------------------------------------------------------------------------

출처: http://rahxephon.tistory.com/tag/java

-------------------------------------------------------------------------------------------------

 

 

.gz .zip 파일 형식 처리 : java.util.zip 패키지에 의해 지원

.jar 파일형식 처리 : java.util.jar 패키지에 의해 지원

 

자료압축  : 디스트 용량 절약 , 네트워크 전송시간을 줄인다.

자료 오류검사 : 저장 전송된 자료가 손상되었는지 검사

파일 묶기 : 여러개의 파일을 하나의 파일로 묶음으로써 전송,배포,백업을 효율적이고 편리하게함

 

 

 

★ 직접 지원하는 압축 파일 형식

 

- GZIP 형식

   단일 파일 압축 형식 : 단일 파일의 압축 및 압축해제

   gzip, gunzip, zcat 프로그램이 지원하는 .gz .z 압축 파일 포맷으로닉스에서 주로 사용된다.

   GZIPOutputStream, GZIPInputStream 클래스에 의해 직접 지원된다.

   ( 유닉스에서 compress 프록램이 지원하는 .z 파일은 직접 지원하지 않는다. )

 

- ZIP 형식

   다중파일 압축형식 : 여러 파일을 하나의 파일로 압축하여 묶거나 풀기

   zip, unzip 프로그램 및 java1.1의 jar 프로그램이 지원하는 .zip .jar파일 형식을 직접지원

   ZIPOuputStream, ZIPInputStream 클래스에 의해 직접지원

   점차 폭 넓게 사용

   원도우즈의 LHA, ARJ, ARC, 압축형식 및 유닉스의 TAR등의 다중 파일 압축형식은 지원하지 않는다.

   ZipException  - extends IOException

 

 

 

 

자료 오류 검사 : 체크섬 검사 스트림

 

자료에 대한 형식을 정하여 해당 자료에 대한 체크썸을 함께 저장. 검색 혹은 수신측에서 자료에 대한 체크섬을 구한 후 함께 저장 전송된 체크섬과 일치하는지에 대한 검사로 일치 하지 않으면 자료가 손상 되었음을

의미한다.

 

GZIP,ZIP 등의 압축 형식에는 이와같이 체크섬을 저장할수 있게 형식이 정해져 있다.

 

CheckedOutputStream 클래스 extends FilterOutputStream

- new CheckedOutputStream(OutputStream out, Checksum cksum)

Checksum getChecksum()

 

CheckedInputStream 클래스 extends FilterInputStream

- new CheckedInputStream(InputStream in, Checksum cksum)

Checksum getChecksum()

 

- Checksum 인터페이스

long getValue() : 현재까지 계산된 체크섬

update( int b ) : 주어진 바이트 b로 부터 현재의 체크썸을 변경

update( byte b[], int off, int len) : 주어진 바이트 배열 b로 부터 현재의 체크썸을 변경

reset() : 현재의 체크섬을 초기화

 

- CRC32 클래스 implements Checksum

new CRC32()

update( byte b[] )

 

- Alder32 클래스 implments Checksum

: CRC32 보다 계산 속도가 빠름

  new Alder32()

  update(byte b[])

 

// Usage: java CheckedStreamTest [ -d ] < infile > outfile
import java.io.*;
import java.util.zip.*;

class CheckedStreamTest
{   public static void main( String[] args )
        throws IOException, Exception
    {  
        if ( args.length == 0 )
            attachCheck();
        else
            detachCheck();
    }

    static void attachCheck() throws IOException, Exception
    {   Checksum check = new Adler32();
        CheckedOutputStream cos = new CheckedOutputStream( System.out, check );
        DataOutputStream dos = new DataOutputStream( cos );
        for(int b; (b = System.in.read()) != -1;)
            dos.write( b );
        dos.writeInt( (int) check.getValue() );
        dos.close();
    }

    static void detachCheck() throws IOException, Exception
    {   Checksum check = new Adler32();
        ByteArrayOutputStream baos = new ByteArrayOutputStream();
        for(int b; (b = System.in.read()) != -1;)
            baos.write( b );
        if ( baos.size() < 4 )
            throw new Exception( "입력 화일에 체크썸 값이 포함되어 있지 않음" );
        byte[] buf = baos.toByteArray();
        for(int b; (b = cis.read()) != -1;)
            System.out.write( b );
        System.out.close();
        DataInputStream trailer
            = new DataInputStream(
                new ByteArrayInputStream( buf, buf.length - 4, 4 ) );
        int savedCheck = trailer.readInt();
        if (savedCheck != (int) check.getValue() )
            throw new Exception( "입력 화일이 손상되었음" );
    }
}

java CheckedStreamTest < check.txt > check.chk

java CheckedStreamTest -d < check.chk > check.txt2

java CheckedStreamTest -d < check.txt > test.txt2

 

 

 

 

자료 압축 및 복원

 

ZIP, GZIP등의 압축 포맷은 내부적으로 사용되는 압축 포맷으로 "DEFLATE"포맷을 사용하며

다음과 같은 클래스에 의해 지원된다.

 

DefaterOutputStream 클래스 extends FilterOutputStream

new DefalterOutputStream(OutputStream out, Deflater def=디폴트, int size=디폴트)

finish() : 연결된 스트림 out을 close()하지 않고 출력을 종료시킨다.

 

InflaterInputStream 클래스 extends FilterInputStream

new InflaterInputStream(InputStream in, Inflater def=디폴트, int size=디폴트)

Checksum getChecksum()

 

 

 

 

Defalter : 자료 압축 제어

 

GZIP, ZIP 압축 형식등에 공통적으로 사용되는 내부 압축 형식 처리 및 압축 알고리즘 제공

new Deflater( int level=DEFAULT_COMPRESSION, boolean nowrap=false)

: nowrap이 true이면 GZIP,및 PKZIP과 호환 되는 압축 포맷을 유지하기 위해 ZLIB 헤더와 체크섬을

  사용하지 않는다.

 

 

입출력 자료제어

 

setInput(byte b[], int off=0, int len=b.length) : 압축될 입력 자료를 셋팅

boolean needsInput() : 입력버퍼가 비어있으면 true

 

int deflate( byte b[], int off=0, int len=b.length)

: 입력 버퍼의 자료를 압축하여 버퍼b 에 저장, 출력 버퍼 b로 출력된 바이트 개수를 반환.

  0 이 반환되면 needsInput() 메소드를 호출하여 입력버퍼가 비어있는지 확인 하여야 한다.

 

finish() : 현재의 입력 버퍼를 처리한 후 압축을 종료한다.

end() : 아직 처리되지 않은 입력 자료를 버린다.

reset() : 새로운 입력 자료를 처리하기 위해 상태를 초기화 한다.

 

 

압축상태조사

 

boolean finished() : 압축이 종료 되었으면 true

int getTotalIn() : 현재 까지 입력된 총 바이트 갯수

int getTotalOut() : 현재까지 출력된 총 바이트 갯수

int getAlder() : 압축되지 않은 상태에서의 자료의 ALDER-32체크썸 값

 

 

압축 알고리즘 제어

setDictionary(byte b[], int off=0, int len=b.length)

: 히스토리 버퍼가 미리 결정될 수 있는 경우에 preset dictionary를 셋팅할 수있다.

 

setStrategy( int strategy) : 압축전략 , 다음과 같은 상수를 사용할수 있다.

- DEFAULT_STRATEGY : 디폴트 압축전략

- FILTERED : 스트링 매칭보다 호프만 코딩을 많이 사용하는 압축전략

                    랜덤한 분포를 갖는 작은 값으로 이루어진 자료를 압축하는데 적당

 

- HUFFMAN_ONLY : 호프만 코딩만을 사용한 압축전략

 

setLevel(int level) : 압축 레벨, 0~9 의 값이 사용될수 있으며 다음과 같은상수 사용할 수 있다.

-DEFAULT_COMPRESSION : 디폴트 압축 레벨

-NO_COMPRESSION: 압축하지 않는 레벨

-BEST_SPEED : 가장 속도가 빠른 압축 레벨

-BEST_COMPRESSION: 가장 압축 효율이 좋은 압축레벨

 

 

 

 

 

Inflater : 압축된 자료의 복원 제어

 

GZIP, ZIP 압축 형식등에 공통적으로 사용되는 내부 압축 형식 처리 및 압축 알고리즘 제공

new Inflater(boolean nowrap=false)

: nowrap이 true이면 GZIP,및 PKZIP과 호환 되는 압축 포맷을 유지하기 위해 ZLIB 헤더와 체크섬을

  사용하지 않는다.

 

 

압축 입출력 자료제어

 

setInput(byte b[], int off=0, int len = b.length) : 압축될 입력자료를 셋팅

boolean needsInput() : 입력버퍼가 비어있으면 true

 

int inflate(byte b[], int off=0, int len=b.length) throws DataFormatException

: 입력 버퍼의 압축된 자료를 복원하여 출력 버퍼 b에 저장. 출력 버퍼b로 출력된 바이트 개수 반환

  0이 반환되면 needsInput() 혹은 needsDictionary()메소드를 호출하여 입력버퍼 혹은 preset dictionary가

  비어있는지 확인 해야한다.

 

finish() : 현재의 입력 버퍼를 처리한 후 압축을 종료한다.

end() : 아직 처리되지 않은 입력자료를 버린다.

reset() : 새로운 입력 자료를 처리하기 위해 상태를 초기화 한다.

 

 

압축 복원상태 조사

boolean finished() : 압축이 종료 되었을때 true

int getTotalIn() : 현재까지 입력된 총 바이트 개수

int getTotalOut() : 현재까지 출력된 총 바이트 개수

int getRemaining() : 입력 버퍼에 남아있는 바이트 개수

int getAlder() : 압축되지 않은 상태에서의 자료의 ALDER-32 체크썸 값

 

 

압축 복원 알고리즘 제어

 

boolean needsDictionary()

: 압축 복원을 위해 preset dictionary가 필요하면 true

 

setDictionary(byte b[], int off=0, int len=b.length)

: 히스토리 버퍼가 미리 결정될 수 있는 경우에, preset dictionary를 셋팅할수 있다.

 

 

 

 

 

GZIP 압축 포맷 처리

 

단일 파일의 압축 및 복원 - CRC-32 체크썸 사용

 

GZIPOutputStream 클래스 extends DefalterOuputStream

- new GZIPOutputStream( OutputStream out, int size=디폴트)

 

GZIPInputStream 클래스 extends InfalterInputStream

- new GZIPInputStream( InputStream in, int size=디폴트)

 

[입력 받은 데이터 파일을 압축]

import java.io.*;
import java.util.zip.*;
  
public class GZIP {
        File file;
        byte[] buf;
 
        public GZIP(File file) {
                this.file=file;
                buf=new byte[1024];
        }
          
        public void 압축저장() throws IOException {
                FileInputStream fis=new FileInputStream(file);
                FileOutputStream fos=new FileOutputStream(file.getAbsolutePath()+".gz");
                GZIPOutputStream gos=new GZIPOutputStream(fos);
 
                for (int i; (i=fis.read(buf))!=-1; ) {
                        gos.write(buf, 0, i);
                }
                gos.close();
                fis.close();
        }
 
        public static void main(String[] args) {
                if (args.length<1) {
                        System.out.println("사용법 : java GZIP 압축할파일이름");
                        System.exit(1);
                }
 
                try {
                        GZIP gzip=new GZIP(new File(args[0]));
                        gzip.압축저장();
                } catch(IOException ioe) {
                        System.out.println("예외발생 : "+ioe.getMessage());
                }
        }
}

 

[Gzip을 이용한 압축해제]

import java.io.*;
import java.util.zip.*;
  
public class UnGZIP {
        File source, target;
        byte[] buf;
 
        public UnGZIP(File source, File target) {
                this.source=source;
                this.target=target;
                buf=new byte[1024];
        }
        public void 압축해제() throws IOException {
                FileInputStream fis=new FileInputStream(source);
                GZIPInputStream gis=new GZIPInputStream(fis);
                FileOutputStream fos=new FileOutputStream(target);
 
                for (int i; (i=gis.read(buf))!=-1; ) {
                        fos.write(buf, 0, i);
                }
                fos.close();
                gis.close();
        }
 
        public static void main(String[] args) {
                if (args.length<2) {
                   System.out.println("사용법 : java UnGZIP 압축풀파일이름 저장할파일이름");
                   System.exit(1);
                }

                try {
                   UnGZIP ungzip=new UnGZIP(new File(args[0]), new File(args[1]));
                   ungzip.압축해제();
                } catch(IOException ioe) {
                   System.out.println("예외발생 : "+ioe.getMessage());
        }
    }
}

 

 

// Usage: java GZIPTest [ -d ] infile
import java.io.*;
import java.util.zip.*;

class GZIPTest
{
    public static void main( String[] args )
        throws IOException
    {
        try
        {   if ( ! args[0].equals("-d") )
                gzip( args[0] );
            else
                gunzip( args[1] );
        } catch( Error ex )
        {   System.err.println( "오류: " + ex.getMessage() );
        }
    }

    static void gzip( String fname ) throws Error, IOException
    {   File infile = new File( fname );
        File outfile = new File( fname + ".gz" );
        if ( outfile.exists() )
            throw new Error( "화일 " + outfile + "이 이미 존재합니다." );
        FileInputStream fis = new FileInputStream( infile );
        FileOutputStream fos = new FileOutputStream( outfile );
        GZIPOutputStream gzos = new GZIPOutputStream( fos );
        byte buf[] = new byte[1024];
        for( int cnt; (cnt = fis.read(buf)) != -1; )
            gzos.write( buf, 0, cnt );
        gzos.close();
        fis.close();
        infile.delete();
    }

    static void gunzip( String fname ) throws Error, IOException
    {   File infile = new File( fname );
        File outfile;
        if ( fname.endsWith(".gz") )
            outfile = new File( fname.substring(0, fname.length() - 3 ) );
        else if ( fname.endsWith(".z") )
            outfile = new File( fname.substring(0, fname.length() - 2 ) );
        else
            throw new Error( "압축 화일 이름이 .gz나 .z로 끝나지 않았음" );
        if ( outfile.exists() )
            throw new Error( "화일 " + outfile + "이 이미 존재함" );
        try
        {
            FileInputStream fis = new FileInputStream( infile );
            GZIPInputStream gzis = new GZIPInputStream( fis );
            FileOutputStream fos = new FileOutputStream( outfile );
            byte buf[] = new byte[1024];
            for( int cnt; (cnt = gzis.read(buf)) != -1; )
                fos.write( buf, 0, cnt );
            fos.close();
            gzis.close();
            infile.delete();
        } catch( ZipException ex)
        {   throw new Error( "ZIP 화일 " + infile + "이 손상되었음" );
        }
    }

    // 중첩된 클래스 (내부 클래스와 다름)
    static class Error extends Exception
    {   public Error(String mesg)
        {   super(mesg); }
    }
}

 

프로그램 오류 발생시 GzipTest.Error 예외를 발생시키고, 이를 처리하는 것이 System.exit(-1)를 사용하는 것보다 더 유연하고 폭 넓게 이용될 수 있는 프로그램 구조를 만든다. ( 가령 애플릿에서 System.exit 메소드를 호출하면 일반적으로 보안 제한에 걸린다.) GZipTest.Error 클래스와 같이 특정 클래스 와 관련 깊은

클래스는 중첩된 클래스로 정의하면 이름짓기가 쉬어 지며 클래스 사이의 관련성을 명시함으로써 가독성을 높인다.

 

java GZIPTest gzip.txt

java GZIPTest -d gzip.txt.gz

 

gzip -d gzip.txt.gz

gzip gzip.txt

 

 

 

 

 

 

ZIP 파일 압축 포맷 처리

 

Zip파일은 GZip 형식과는 다르게 ZipEntry라는 객체를 사용한다. Zip파일안에는 여러개의 파일이 들어갈 수 있는데 이 파일 하나 하나를 java.util.zip.ZipEntry 객체로 다루기 때문이다.

 

여러 파일을 단일 파일로 묶기/풀기  압축/복원 - CRC-32 체크썸 사용

 

 

ZIP 파일 엔트리 : zip 파일에 포함되는 각 파일에 대하여 그 내용과 관련정보

                         ( 파일이름, 마지막 수정 시각등등) 을 나타낸다.

 

ZipOutputStream 클래스 extends DeflaterOutputStream

- new ZipOutputStream ( OutputStream out )

 

putNextEntry ( Zipentry e)

: ZIP 파일 엔트리의 출력을 시작한다. 출력중인 이전 엔트리가 있으면 closeEntry()를 호출한다.

  파일 수정 시각을 해당 ZipEntry객체이서 셋팅하지 않은 경우 현재의 시각으로 셋팅한다.

  이전에 출력중이던 엔트리가 있으면 closeEntry() 를 불러준다.

 

closeEntry()

: 현재의 ZIP 파일 엔트리의 출력을 끝낸다.

 

setComment(String comment)

: ZIP 파일 전체에 대한 주석 문자열 셋팅

 

setMethod(int method)

: ZIP 파일 엔트리를 출력할 때 사용된 ZipEntry객체에서 압축여부를 셋팅하지 않았을 경우 압축여부를

  나타낸다. 다음과 같은 상수를 사용할 수 있다. DEFLATED로 초기화 된다.

   - DEFLATED : 압축됨

   - STORED : 압축안됨

 

setLevel(int level)

: ZIP파일에 포함될 파일내용의 압축 레벨을 셋팅한다. ( DEFALUT_COMPRESSION)으로 초기화

 

 

★ ZipInputStream 클래스 extends InflaterInputStream

 

new ZipInputStream(InputStream in)

 

ZipEntry getNextEntry()

: 다음 zip 파일 엔트리의 관련정보를 읽어들여 관련정보를 담는 ZipEntry객체를 생성한다.

  이전에 입력 중이던 엔트리가 있으면 closeEntry()를 불러준다.

  closeEntry 혹은 read메소드를 호출하여 현재의 엔트리를 완전히 읽어 들여야만 ZipEntry 객체 정보가

  완전하게 셋팅된다.

 

read등의 재정의된 메소드

: 현재의 ZIP 파일 엔트리로 부터 자료를 읽으며, 현재의 엔트리의 끝을 만나면 -1을 반환

 

closeEntry():

: 입력중인 ZIP 파일 엔트리의 입력을 끝내고 다음 엔트리의 입력을 준비한다.

 

[Zip 압축]

import java.io.*;
import java.util.zip.*;
  
public class Zip {
        File directory;
        byte[] buf;
 
        public Zip(File directory) throws IllegalArgumentException {
                if (!directory.isDirectory()) {
                   throw new IllegalArgumentException(directory+" 디렉토리가  아님");
                }

                this.directory=directory;
                buf=new byte[1024];
        }

        public void 압축저장(String zipName) throws IOException {
               File[] files=directory.listFiles();

               if (files.length<1) {
                  return;
               }

               FileOutputStream fos=new FileOutputStream(System.getProperty("user.dir")+zipName);
               ZipOutputStream zos=new ZipOutputStream(fos);

               for (int i=0; i<files.length; i++) {
                    if (files[i].isDirectory()) {
                       continue;
                    }

                    FileInputStream fis=new FileInputStream(files[i]);
                    System.out.println(files[i]);
                    ZipEntry entry=new ZipEntry(files[i].toString());
                    zos.putNextEntry(entry);

                    for (int j; (j=fis.read(buf))!=-1; ) {
                          zos.write(buf, 0, j);
                    }
                    zos.closeEntry();
                    fis.close();
             }
             zos.close();
    }

    public static void main(String[] args) {
             if (args.length<2) {
                  System.out.println("사용법 : java Zip 압축할디렉토리 압축파일이름");
                   System.exit(1);
             }

            try {
                  Zip zip=new Zip(new File(args[0]));
                  if (!args[1].startsWith("/") || !args[1].startsWith("\\")) {
                       args[1]=File.separator+args[1];
                  }
                  zip.압축저장(args[1]);
            } catch(IOException ioe) {
                 System.out.println("예외발생 : "+ioe.getMessage());
            } catch(IllegalArgumentException iae) {
                 System.out.println(iae.getMessage());
            }
    }
}

하위디렉토리에 대한 압축이 빠져 있다.

 

[Zip 압축해제]

import java.io.*;
import java.util.zip.*;  
public class UnZip {
        File archive;
        byte[] buf;
 
        public UnZip(File archive) {
                this.archive=archive;
                buf=new byte[1024];
        }
 
        public void 압축해제() throws IOException {
                FileInputStream fis=new FileInputStream(archive);
                ZipInputStream zis=new ZipInputStream(fis);
 
                for (ZipEntry entry; (entry=zis.getNextEntry())!=null; ) {
                        File file=new File(entry.getName());
                        System.out.println(file);
                        if (entry.isDirectory()) {
                                boolean result=file.mkdirs();
                                if (!result) {
                                        throw new IOException(file+" 디렉토리 생성실패");
                                }
                                continue;
                        }
 
                        if (!file.getParentFile().exists()) { // imsi 디렉토리가 존재하는지확인
                                file.getParentFile().mkdirs();
                        }
 
                        FileOutputStream fos=new FileOutputStream(file);
                        for (int i; (i=zis.read(buf))!=-1; ) { // -1 ZipEntry가 끝남 (Stream아님)
                                fos.write(buf, 0, i);
                        }
                        fos.close();
                        zis.closeEntry();
                }
                zis.close();
        }
 
        public static void main(String[] args) {
                if (args.length<1) {
                        System.out.println("사용법 : java UnZip 압축해제할파일이름");
                        System.exit(1);
                }
 
                try  {
                        UnZip unzip=new UnZip(new File(args[0]));
                        unzip.압축해제();
                } catch(IOException ioe) {
                        System.out.println("예외발생 : "+ioe.getMessage());
                        ioe.printStackTrace();
                }
        }
}

 

ZipInputStream형 객체 zis에서 nextEntry()메소드로 ZipEntry 객체를 얻고 이 객체의 getName()으로

파일 이름을 얻고 이 파일이름을 기반으로 FileOutputStream객체를 만든다.

여기서, ZipEntry가 디렉토리인지 일반 파일인지 isDirectory() 메소드를 통해서 구분한다.

 

[Zip 뷰어]

import java.io.*;
import java.util.*;
import java.util.zip.*;
import java.text.*;
  
public class ZipFileViewer {
        public static void main(String[] args) {
                if (args.length<1) {
                      System.out.println("사용법 : java ZipFileViewer  압축파일이름");
                      System.exit(1);
                }

                try {
                      ZipFile zip=new ZipFile(args[0]);
                      SimpleDateFormat sdf=new SimpleDateFormat();
                      Enumeration enum=zip.entries();
                      System.out.println("파일리스트 ---");
                      while(enum.hasMoreElements()) {
                           ZipEntry entry=(ZipEntry)enum.nextElement();
                           System.out.print(entry.getCompressedSize());
                           System.out.print("\t");
                           System.out.print(entry.getSize());
                           System.out.print("\t");
                           System.out.print(entry.getName());
                           System.out.print("\t");
                           if (entry.getName().length()<16) {
                               System.out.print("\t"); // 긴 파일이름을 출력할 때 문제가있슴
                          }                                       // java.text.MessageFormat을 이용해결
                          System.out.println(sdf.format(new Date(entry.getTime())));
                      }
               } catch(IOException ioe) {
                      System.out.println(args[0]+" 파일을 읽는데 문제가 있음");
                      System.exit(1);
               }
       }
}

 

 

 

 

 

ZipEntry 클래스 : ZIP 파일에 포함되는 파일정보 셋팅, 추출

 

new ZipEntry(String name)

: 포함된 파일 경로 이름 name을 나타내는 ZipEntry객체 생성. name에서 디렉토리를 구분하는 문자는

  플랫폼에 상관없이 '/'를 사용하여야 호환성에 문제가 생기지 않는다. 디렉토리 일경우 '/' 로 끝나야한다.

 

String name : 파일경로이름

String toString() : 파일 경로 이름

long time : 파일이 수정된 시각 (Data클래스와 동일) 해당 정보가 없으면 -1

long size : 압축되지 않은 상태에서의 파일 크기 , 해당정보가 없으면 -1

long compressedSize : 압축된 경우에는 압축된 상태에서의 파일크기 해당정보가 없으면 -1

long crc : 압축되지 않은 상태에서의 CRC-32 체크썸 해당정보가 없으면 -1

boolean isDirectory() : 포함된 파일이 디렉토리를 나타내면 true

 

int method : 압축여부를 나타낸다. 다음과 같은 상수가 정의됨

 - DEFLATED : 압축됨

 - STORED : 압축안됨

 

byte[] extra : 선택적으로 부가적인 정보가 포함될 수 있다 없으면 null

String comment : 선택적으로 주석 문자열이 포함될 수 있다.

 

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

class ZipTest
{
    static boolean isRecursive = false;
    static ZipOutputStream zos;

    public static void main( String[] args )
        throws IOException
    {
        try
        {   int argnum = 0;
            if ( args.length == 0 )
            {
                System.out.println( "Usage: java ZipTest [ -r ] zipfile  file1 [ file2 ... ]" );
                System.exit(-1);
            } else if ( args[argnum].equals("-r") )
            {   isRecursive = true;
                argnum++;
            }
            File zipfile = new File( args[argnum++] );
            if ( zipfile.exists() )
                throw new Error( "ZIP 화일 " + zipfile + "이 이미 존재함." );
            zos = new ZipOutputStream( new FileOutputStream( zipfile ) );
            if ( args.length <= argnum )
                throw new Error(
                    "사용법: java ZipTest [ -r ] zipfile  file1 [ file2 ... ]" );
            for(int i = argnum; i < args.length; ++i)
                outputEntry( new File(args[i]) );
            zos.close();
        } catch( Error ex )
        {   System.err.println( "오류: " + ex.getMessage() );
        }
    }
   
    public static void outputEntry( File f ) throws Error, IOException
    {   if ( ! f.exists() )
             throw new Error( "화일 " + f + "이 존재하지 않음" );
        String adjustedPath = f.getPath().replace(File.separatorChar, '/');
        if ( f.isDirectory() && ! adjustedPath.endsWith("/") )
            adjustedPath += '/';
        ZipEntry entry = new ZipEntry( adjustedPath );
        entry.setTime( f.lastModified() );
            // File 클래스에서 lastModified() 메쏘드의 시각 기준이 문서화되어
            // 있지는 않으나, 솔라리스와 윈도우즈에서는 Date 클래스와 같은
            // 시각 기준이 사용되고 있음.
        zos.putNextEntry( entry );
       
        if ( f.isDirectory() )
        {   System.out.println( "디렉토리 첨가: " + f );
            if ( isRecursive )
            {   String[] files = f.list();
                for( int i = 0; i < files.length; ++i )
                    outputEntry( new File(f, files[i]) );  // 재귀적 호출 사용
            }
        } else
        {   System.out.println( "    화일 첨가: " + f );
            FileInputStream fis = new FileInputStream( f );
            byte buf[] = new byte[1024];
            for( int cnt; (cnt = fis.read(buf)) != -1; )
                zos.write( buf, 0, cnt );
            fis.close();
        }
    }

    static class Error extends Exception
    {   public Error(String mesg)
        {   super(mesg); }
    }
}

 

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

class UnzipTest
{
    public static void main( String[] args )
        throws IOException
    {
        try
        {
            boolean isListing = false;
            int argnum = 0;
            if ( args.length == 0 )
            {
                System.out.println( "Usage: java UnzipTest [ -l ] zipfile" );
                System.exit(-1);
            } else if ( args[0].equals("-l") )
            {   isListing = true;
                argnum++;
            }
            File zipfile = new File( args[argnum] );
            if ( ! zipfile.exists() )
                throw new Error( "ZIP 화일 " + zipfile + "이 존재하지 않음." );
            ZipInputStream zis
                = new ZipInputStream( new FileInputStream( zipfile ) );
            if ( isListing )
                list(zis);
            else
                extract(zis);
            System.out.close();
        } catch( Error ex )
        {   System.err.println( "오류: " + ex.getMessage() );
        }
    }       

    static void list(ZipInputStream zis) throws IOException
    {
        ZipEntry entry;
        System.out.println( " 크기 (압축 크기)   날짜    시각    이름" );
        System.out.println( " ----------------   ----    ----    ----" );
        MessageFormat fmt = new MessageFormat(
            "{0,date,MM-dd-yy}  {0,time,HH:mm}   {1}" );
        while( (entry = zis.getNextEntry()) != null )
        {   zis.closeEntry();
            System.out.println(
                padToWidth( String.valueOf(entry.getSize()), 7 )
                + padToWidth( "(" + String.valueOf(entry.getCompressedSize())
                              + ")", 10 )
                + " " + fmt.format( new Object[] {
                        new Date( entry.getTime() ),
                        entry.getName() } )
                );
        }
    }

    static String padToWidth(String s, int width)
    {   if ( s.length() >= width )
            return s;
        char padded[] = new char[width - s.length()];
        for( int i = 0; i < padded.length; i++ )
            padded[i] = ' ';
        return padded + s;
    }

    static void extract(ZipInputStream zis) throws IOException, Error
    {
        ZipEntry entry;
        while( (entry = zis.getNextEntry()) != null )
        {   File entryFile
                = new File( entry.getName().replace('/', File.separatorChar) );
            if ( entry.isDirectory() )
            {   if ( ! entryFile.exists() )
                {   System.out.println( " 디렉토리 생성: " + entryFile );
                    entryFile.mkdirs();
                }
                continue;
            }

            if ( entryFile.getParent() != null )
            {   File parent = new File( entryFile.getParent() );
                if ( ! parent.exists() )
                    parent.mkdirs();
            }
            if ( entry.getMethod() == ZipEntry.STORED )
            {   System.out.println( "          추출: " + entryFile );
            } else if ( entry.getMethod() == ZipEntry.DEFLATED )
            {   System.out.println( "     압축 풀기: " + entryFile );
            } else
                throw new Error( entryFile
                       + "화일에서 처리할 수 없는 압축 방식이 사용되었음" );
            if ( entryFile.exists() )
                throw new Error( entryFile + "이 이미 존재함" );
            FileOutputStream fos = new FileOutputStream( entryFile );
            byte buf[] = new byte[1024];
            for( int cnt; (cnt = zis.read(buf)) != -1; )
                fos.write( buf, 0, cnt );
            fos.close();
        }
    }

    static class Error extends Exception
    {   public Error(String mesg)
        {   super(mesg); }
    }
}

 

mkdir test

copy *.java test

dir test

java -Duser.timezon=JST ZipTest -r test,zip test

java -Duser.timzzone=JST UnzipTest -1 test.zip

( java UnzipTest test.zip )

 

 

 

 

ZipFile 클래스 : ZIP 파일에 포함된 파일 조사 , 추출

스트림 클래스 종류가 아니므로 ZIP 자료가 파일 형태로 저장되어 있는 경우에만 사용될 수 있다.

 

new ZipFile(String name) : String name : ZIP 파일의 경로이름

new ZipFile (File file)

 

Enumeration entries() : 포함된 모든 zip 파일 엔트리에 대한 ZipEntry객체의 Enumeration

 

Zipentry getEntry(String name)

: 주어진 파일경로 이름을 갖는 파일이 ZIP파일에 포함되어 있으면 해당 ZipEntry 객체를 반환하고 없으면

   null

 

InputStream getInputStream(Zipentry ze)

: ze가 나타내는 포함된 파일을 추출하기 위한 Inputstream 객체

 

close()

 

 

 

 

jar 형식

 

Jar 형식의 파일을 다루려면 java.util.jar 패키지에 있는 클래스들을 사용해야 한다.

이 들 클래스들은 java.util.zip에서 상속 받아온 클래스 들이다.

 

Jar 형식을 입출력하는 클래스로는 java.util.jarInputStream과 java.util.jarOutputStream 이 있다.

JarInputStream에는 getNextJarEntry() 메소드와 getMamifest() 메소드가 추가 되었습니다.

생성자에는 boolean verify라는 인자를 더 줄 수 있다. Verify는 Jar파일이 전자 사인되었는지를 체크합니다.

 

 

- JarInputStream

nextJarEntry()

getAttribute(),getCerificates() : Jar 파일의 Manifest정보를 읽어 들인다.

 

 

- JarOutputStream 에서는 Manifest 객체를 주어서 Manifest 정보를 파일에 저장할 수 있게

  하고있다.

 

putNextEntry(ZipEntry entry)메소드는 JarEntry객체의 내용을 저장한다.

 

 

 

 

 

Executable Jar 만들기

 

jar 파일에는 meta-inf라는 디렉토리 밑에 manifest.mf라는 파일이 들어가 수 있다.

이 파일 안에 다음과 같은 헤더를 하나 더 추가시켜주면 jar파일은 Execute Jar 파일이 됩니다.

 

Main-Class: 클래스 이름

 

여기서 주의할 것은 다른 헤더들 사이에 빈줄이 있으면 안 된다는 것이다.

Manifest-Version: 1.0

Main-Class: Line

import java.awt.*;
import java.awt.event.*;
import java.awt.geom.*;
public class Line extends Frame {
        BasicStroke stroke;
        GradientPaint grad;
        public Line() {
                super("Line");
                stroke=new BasicStroke(10.0f);
                grad=new GradientPaint(0f, 0f, Color.red, 100f, 0f, Color.yellow);
               
                setBackground(Color.black);
                addWindowListener(new WindowAdapter() {
                        public void windowClosing(WindowEvent we) {
                                System.exit(0);
                        }
                });
        }
       
        public void paint(Graphics g) {
                Graphics2D g2=(Graphics2D)g;
                g2.setRenderingHint(RenderingHints.KEY_ANTIALIASING,
RenderingHints.VALUE_ANTIALIAS_ON);
                g2.setStroke(stroke);
                g2.setPaint(grad);
               
                g2.draw(new Line2D.Double(0, 0, 200, 200));
                g2.drawString("좌표 (0, 0)부터 사선", 10, 150);
        }
 
        public static void main(String[] args) {
                Line line=new Line();
                line.setSize(200, 200);
                line.setVisible(true);
        }
}

 

이 소스를 컴파일 하면 Line.class와 Line$1.class가 생긴다.

jar cvfm Line.jar mf Line*.class

 

실행:  java -jar Line.jar

 

[ Jar 사용법 ]

c - create

v - verbose ( 상황보고 )

f - 파일명 ( 먼저 왓으니 Line.jar)

m - mf (Manifest파일명)( 만일 cfmf 였다면 mf 다음 Line.jar이 와야함 )