Javaによる圧縮と解凍(zip、gzip)による中国語パスのサポート


zipはアーカイブと圧縮の2つの役割を果たしています.gzipはファイルをアーカイブするのではなく、単一のファイルを圧縮するだけなので、UNIXプラットフォームでは、コマンドtarは通常、ファイルを作成し、gzipにファイルを圧縮するようにコマンドします.Java I/Oクラスライブラリには、圧縮フォーマットストリームを読み書きできるクラスもいくつか収録されています.圧縮機能を提供するには、既存のI/Oクラスの外にパッケージすればよい.これらのクラスはReaderとWriterではなく、InputStreamとOutStreamputのサブクラスです.これは、圧縮アルゴリズムが文字ではなくbyte向けであるためです.関連クラスとインタフェース:Checksumインタフェース:クラスAdler 32とCRC 32によって実現されるインタフェースAdler 32:Alder 32アルゴリズムを使用してChecksum数CRC 32を計算する:CRC 32アルゴリズムを使用してChecksum数を計算する
CheckedInputStream:InputStream派生クラスは、入力ストリームの検証とChecksumを得ることができ、データの整合性を検証するためのCheckedOutputStream:OutputStream派生クラスは、出力ストリームの検証とChecksumを得ることができ、データの整合性を検証するための
DeflaterOutputStream:クラスのベースクラスを圧縮します.ZipOutputStream:DeflaterOutputStreamのサブクラスで、データをZipファイル形式に圧縮します.GZIPOutputStream:DeflaterOutputStreamのサブクラスで、データをGZipファイル形式に圧縮
InflaterInputStream:解凍クラスのベースクラスZipInputStream:InflaterInputStreamのサブクラスで、Zip形式のデータGZIPInputStream:InflaterInputStreamのサブクラスを解凍でき、Zip形式のデータを解凍できる
ZipEntryクラス:ZIPファイルエントリを表すZipFileクラス:ZIPファイルからエントリを読み込むためのクラス
GZIPによる単一ファイル圧縮
GZIPのインタフェースは簡単なので、1つのストリームを圧縮するだけで使用できます.もちろん、文字ストリームを圧縮することができ、バイトストリームを圧縮することができ、以下はGBK符号化フォーマットのテキストファイルを圧縮することができます.圧縮クラスの使い方は非常に簡単です.GZIPOutputStreamまたはZipOutputStreamで出力ストリームを包み、GZIPInputStreamまたはZipInputStreamで入力ストリームを包むだけです.残りは普通のI/O操作です.
import java.io.BufferedOutputStream;
import java.io.BufferedReader;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStreamReader;
import java.util.zip.GZIPInputStream;
import java.util.zip.GZIPOutputStream;

public class GZIPcompress {
	public static void main(String[] args) throws IOException {
		//           , ,         GBK     
		BufferedReader in = new BufferedReader(new InputStreamReader(new FileInputStream(
				"e:/tmp/source.txt"), "GBK"));
		//  GZIPOutputStream  OutputStream ,        ,     test.txt.gz   
		//         test.txt   
		BufferedOutputStream out = new BufferedOutputStream(new GZIPOutputStream(
				new FileOutputStream("test.txt.gz")));
		System.out.println("       ...");
		int c;
		while ((c = in.read()) != -1) {

			/* 
			 *  ,           ,          ,      c,  c  Unicode
			 *  ,        (           ),      GBK      。
			 */
			out.write(String.valueOf((char) c).getBytes("GBK"));
		}
		in.close();
		out.close();
		System.out.println("       ...");
		//  GZIPInputStream  InputStream ,        
		BufferedReader in2 = new BufferedReader(new InputStreamReader(
				new GZIPInputStream(new FileInputStream("test.txt.gz")), "GBK"));
		String s;
		//          
		while ((s = in2.readLine()) != null) {
			System.out.println(s);
		}
		in2.close();
	}
}

Zipによる複数ファイル圧縮
JavaはZipフォーマットクラスライブラリを比較的包括的にサポートしており、複数のファイルを1つの圧縮パッケージに圧縮することができます.このクラスライブラリは標準Zip形式を使用しているので、多くの圧縮ツールと互換性があります.ZipOutputStreamクラスには圧縮方法の設定と圧縮方式で使用する圧縮レベルの設定がある、zipOutputStream.setMethod(int method)エントリのデフォルト圧縮方法を設定します.単一のZIPファイルのエントリに圧縮方法を指定しない限り、ZipOutputStreamで設定圧縮方法を用いて格納、デフォルト値はZipOutputStreamである.DEFLATED(圧縮ストレージを行うことを示す)は、STORED(アーカイブストレージのみをパッケージ化することを示す)に設定することもできる.ZipOutputStreamは、圧縮方法がDEFLATEDであることを設定した後、さらにsetLevel(int level)方法を用いて圧縮レベルを設定することもできる.圧縮レベル値は0-9の計10レベル(値が大きいほど圧縮の利害を示す)であり、デフォルトはDeflate.DEFAULT_COMPRESSION=-1.もちろん,エントリZipEntryのsetMethodメソッドにより,単一条件に圧縮メソッドを設定することもできる.クラスZipEntryは、ZIPファイルに格納された圧縮ファイルを記述する.クラスには、ZIPエントリの情報を設定および取得するための様々な方法が含まれています.クラスZipEntryは、ZipFile[zipFile.getInputStream(ZipEntry)]とZipInputStreamで使用されてZIPファイルを読み込み、ZipOutputStreamでZIPファイルを書き込む.getName()がエントリ名を返す、isDirectory()がディレクトリエントリである場合はtrue(ディレクトリエントリが'/'で終わるエントリとして定義)、setMethod(int method)がエントリを設定圧縮方法を返し、ZipOutputStreamとすることができる.STOREDまたはZipOutputStream.DEFLATED.
 
次の例ではapacheのzipツールパッケージを使用します.(所在パッケージはant.jar)javaタイプが持つ中国語パスはサポートされていないため、両者の使用方法は同じであるが、apache圧縮ツールが多く符号化方式のインタフェースを設定しているだけで、その他は基本的に同じである.またorg.apache.tools.zip.ZipOutputStreamを使用して圧縮する場合、org.apache.tools.zip.ZipEntryを使用して解凍するしかないJavaは使用できません.util.zip.ZipInputStreamは読み取りを解凍します.もちろんapacheはZipInputStreamクラスを提供していません.
 
import java.io.BufferedInputStream;
import java.io.BufferedOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.util.Enumeration;
import java.util.zip.CRC32;
import java.util.zip.CheckedInputStream;
import java.util.zip.CheckedOutputStream;
import java.util.zip.Deflater;
import java.util.zip.ZipException;
import java.util.zip.ZipInputStream;

import org.apache.tools.zip.ZipEntry;
import org.apache.tools.zip.ZipFile;
import org.apache.tools.zip.ZipOutputStream;

/**
 * 
 *              ,              、    
 * 
 * @author jzj
 */
public class ZipCompress {

	private static boolean isCreateSrcDir = true;//       

	/**
	 * @param args
	 * @throws IOException
	 */
	public static void main(String[] args) throws IOException {
		String src = "m:/      .txt";//     ,        
		String decompressDir = "e:/tmp/decompress";//    
		String archive = "e:/tmp/test.zip";//     
		String comment = "Java Zip   .";//     

		//----       
		writeByApacheZipOutputStream(src, archive, comment);

		/*
		 *      ,   ,      apache    ,    java   
		 *       ,      
		 */
		//readByZipInputStream();
		//----  apace ZipFile      
		readByApacheZipFile(archive, decompressDir);
	}

	public static void writeByApacheZipOutputStream(String src, String archive,
			String comment) throws FileNotFoundException, IOException {
		//----    :
		FileOutputStream f = new FileOutputStream(archive);
		//            
		CheckedOutputStream csum = new CheckedOutputStream(f, new CRC32());

		ZipOutputStream zos = new ZipOutputStream(csum);
		//    
		zos.setEncoding("GBK");
		BufferedOutputStream out = new BufferedOutputStream(zos);
		//       
		zos.setComment(comment);
		//    
		zos.setMethod(ZipOutputStream.DEFLATED);
		//         ,         
		zos.setLevel(Deflater.BEST_COMPRESSION);

		File srcFile = new File(src);

		if (!srcFile.exists() || (srcFile.isDirectory() && srcFile.list().length == 0)) {
			throw new FileNotFoundException(
					"File must exist and  ZIP file must have at least one entry.");
		}
		//          
		src = src.replaceAll("\\\\", "/");
		String prefixDir = null;
		if (srcFile.isFile()) {
			prefixDir = src.substring(0, src.lastIndexOf("/") + 1);
		} else {
			prefixDir = (src.replaceAll("/$", "") + "/");
		}

		//       
		if (prefixDir.indexOf("/") != (prefixDir.length() - 1) && isCreateSrcDir) {
			prefixDir = prefixDir.replaceAll("[^/]+/$", "");
		}

		//    
		writeRecursive(zos, out, srcFile, prefixDir);

		out.close();
		//  :            ,            
		System.out.println("Checksum: " + csum.getChecksum().getValue());
		BufferedInputStream bi;
	}

	/**
	 *    org.apache.tools.zip.ZipFile     ,   java     
	 * java.util.zip.ZipFile         ,            
	 *   。
	 * 
	 *  ,apache      ZipInputStream  ,          ZipFile
	 *        。
	 * @param archive      
	 * @param decompressDir     
	 * @throws IOException
	 * @throws FileNotFoundException
	 * @throws ZipException
	 */
	public static void readByApacheZipFile(String archive, String decompressDir)
			throws IOException, FileNotFoundException, ZipException {
		BufferedInputStream bi;

		ZipFile zf = new ZipFile(archive, "GBK");//    

		Enumeration e = zf.getEntries();
		while (e.hasMoreElements()) {
			ZipEntry ze2 = (ZipEntry) e.nextElement();
			String entryName = ze2.getName();
			String path = decompressDir + "/" + entryName;
			if (ze2.isDirectory()) {
				System.out.println("         - " + entryName);
				File decompressDirFile = new File(path);
				if (!decompressDirFile.exists()) {
					decompressDirFile.mkdirs();
				}
			} else {
				System.out.println("         - " + entryName);
				String fileDir = path.substring(0, path.lastIndexOf("/"));
				File fileDirFile = new File(fileDir);
				if (!fileDirFile.exists()) {
					fileDirFile.mkdirs();
				}
				BufferedOutputStream bos = new BufferedOutputStream(new FileOutputStream(
						decompressDir + "/" + entryName));

				bi = new BufferedInputStream(zf.getInputStream(ze2));
				byte[] readContent = new byte[1024];
				int readCount = bi.read(readContent);
				while (readCount != -1) {
					bos.write(readContent, 0, readCount);
					readCount = bi.read(readContent);
				}
				bos.close();
			}
		}
		zf.close();
	}

	/**
	 *    java api    ZipInputStream      ,         
	 * org.apache.tools.zip.ZipOutputStream ,    java     
	 * java.util.zip.ZipOutputStream ,       ,       
	 *       ,         :
	 * java.lang.IllegalArgumentException
	 * at java.util.zip.ZipInputStream.getUTF8String(ZipInputStream.java:290)
	 * 
	 *   ,         java   java.util.zip.ZipOutputStream
	 *            ,       
	 * 
	 * @param archive      
	 * @param decompressDir     
	 * @throws FileNotFoundException
	 * @throws IOException
	 */
	public static void readByZipInputStream(String archive, String decompressDir)
			throws FileNotFoundException, IOException {
		BufferedInputStream bi;
		//----    (ZIP                    ):
		System.out.println("       ");

		FileInputStream fi = new FileInputStream(archive);
		CheckedInputStream csumi = new CheckedInputStream(fi, new CRC32());
		ZipInputStream in2 = new ZipInputStream(csumi);
		bi = new BufferedInputStream(in2);
		java.util.zip.ZipEntry ze;//      
		//           
		while ((ze = in2.getNextEntry()) != null) {
			String entryName = ze.getName();
			if (ze.isDirectory()) {
				System.out.println("         - " + entryName);
				File decompressDirFile = new File(decompressDir + "/" + entryName);
				if (!decompressDirFile.exists()) {
					decompressDirFile.mkdirs();
				}
			} else {
				System.out.println("         - " + entryName);
				BufferedOutputStream bos = new BufferedOutputStream(new FileOutputStream(
						decompressDir + "/" + entryName));
				byte[] buffer = new byte[1024];
				int readCount = bi.read(buffer);

				while (readCount != -1) {
					bos.write(buffer, 0, readCount);
					readCount = bi.read(buffer);
				}
				bos.close();
			}
		}
		bi.close();
		System.out.println("Checksum: " + csumi.getChecksum().getValue());
	}

	/**
	 *     
	 * 
	 *    org.apache.tools.zip.ZipOutputStream      ,            ,
	 *  Java     java.util.zip.ZipOutputStream                 。
	 *    apache        java           ,          。
	 *  
	 * @param zos
	 * @param bo
	 * @param srcFile
	 * @param prefixDir
	 * @throws IOException
	 * @throws FileNotFoundException
	 */
	private static void writeRecursive(ZipOutputStream zos, BufferedOutputStream bo,
			File srcFile, String prefixDir) throws IOException, FileNotFoundException {
		ZipEntry zipEntry;

		String filePath = srcFile.getAbsolutePath().replaceAll("\\\\", "/").replaceAll(
				"//", "/");
		if (srcFile.isDirectory()) {
			filePath = filePath.replaceAll("/$", "") + "/";
		}
		String entryName = filePath.replace(prefixDir, "").replaceAll("/$", "");
		if (srcFile.isDirectory()) {
			if (!"".equals(entryName)) {
				System.out.println("       - " + srcFile.getAbsolutePath()
						+ "  entryName=" + entryName);

				//     ,            / 
				zipEntry = new ZipEntry(entryName + "/");
				zos.putNextEntry(zipEntry);
			}

			File srcFiles[] = srcFile.listFiles();
			for (int i = 0; i < srcFiles.length; i++) {
				writeRecursive(zos, bo, srcFiles[i], prefixDir);
			}
		} else {
			System.out.println("      - " + srcFile.getAbsolutePath() + "  entryName="
					+ entryName);
			BufferedInputStream bi = new BufferedInputStream(new FileInputStream(srcFile));

			//      ZIP                  
			zipEntry = new ZipEntry(entryName);
			zos.putNextEntry(zipEntry);
			byte[] buffer = new byte[1024];
			int readCount = bi.read(buffer);

			while (readCount != -1) {
				bo.write(buffer, 0, readCount);
				readCount = bi.read(buffer);
			}
			// ,            ,             , 
			//                   
			bo.flush();
			//       
			bi.close();
		}
	}
}
 
圧縮パッケージにファイルを追加するには、ZipEntryオブジェクトをputNextEntry()に渡す必要があります.ZipEntryはインタフェースが複雑なオブジェクトで、Zipファイルのレコードの情報を設定して読み取ることができます.これらの情報には、ファイル名、圧縮前と圧縮後のサイズ、日付、CRCチェックコード、追加フィールド、注釈、圧縮方法、ディレクトリかどうかが含まれています.標準的なZip形式ではパスワードがサポートされていますが、JavaのZipクラスライブラリではサポートされていません.また、ZipEntryはCRCのインタフェースのみを提供しているが、CheckedInputStreamとCheckedOutputStreamはAdler 32とCRC 32の2つの検証コードをサポートしている.これは最下位のZipフォーマットの制限ですが、より速いAdler 32の使用を妨げることになります.ファイルを抽出するには、ZipInputStreamのgetNextEntry()メソッドを使用します.圧縮パッケージにZipEntryがある限り、それを抽出します.また、ZipFileオブジェクトでファイルを読む方法もあります.ZipFileにはentries()メソッドがあり、ZipEntriesのEnumerationを返すことができます.そしてzipFileを通ります.getInputStream(ZipEntry entry)圧縮ストリームを取得すると、対応するエントリを読み込むことができます.
チェックコードを読み込むには、Checksumオブジェクトを取得する必要があります.ここではCheckedOutputStreamとCheckedInputStreamを使っていますが、Checksumも使えます.java.util.zipパッケージにおける比較的重要な検証アルゴリズムクラスは、javaを実現するAdler 32およびCRC 32である.util.zip.Checksumインタフェース、圧縮データのチェックサムを推定した(checksum).演算速度の面では、Adler 32アルゴリズムはCRC 32アルゴリズムよりも優れているが、データの信頼性の面ではCRC 32アルゴリズムの方が優れている.GetValueメソッドは現在のchecksum値を得るために使用することができ、resetメソッドはchecksumが不足している値を再設定することができる.チェックサムは一般的にファイルと情報の正確な伝送を検証するために使用される.例を挙げると、ZIPファイルを作成し、リモートコンピュータに転送したいとします.リモートコンピュータに到着すると、checksumを使用して転送中にファイルにエラーが発生したかどうかを確認することができます.ファイルをダウンロードした後、ハッシュ値を使用してファイルのダウンロードプロセスにエラーが発生したかどうかを確認することができます.
Zipクラスにはもう一つのわけのわからないsetComment()メソッドがあります.例えばZipCompress.JAvaに示すように、ファイルを書くときはコメントを付けることができますが、ファイルを読むときはZipInputSreamはインタフェースを提供しません.その注釈機能は完全にエントリ目的に向けられているようで、ZipEntryで実現されています.もちろん、GZIPとZipはファイルを圧縮するだけでなく、ネットワークを通じて伝送するデータを含むものを圧縮することもできます.