Java7 から、標準ライブラリで SJIS ファイル名の ZIP圧縮/解答 ができるようになった → JavaSE ZIP圧縮/解凍 Java7
package com.snail.zip;
import java.io.BufferedInputStream;
import java.io.BufferedOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.util.zip.CRC32;
import java.util.zip.CheckedInputStream;
import java.util.zip.CheckedOutputStream;
import java.util.zip.Deflater;
import java.util.zip.ZipEntry;
import java.util.zip.ZipInputStream;
import java.util.zip.ZipOutputStream;
// Zipファイルに日本語のファイル名を格納したいときには、ant.jar にある
// 以下のクラスに差し替える
// import org.apache.tools.zip.ZipEntry;
// import org.apache.tools.zip.ZipOutputStream;
public class ZipUtil {
/**
* 登録できるディレクトリの最大深さ(シンボリックリンクによる循環参照を防ぐため)
*/
private static final int MAX_DEPTH = 255;
/**
* ファイルを読み込むときのバッファイサイズ
*/
private static final int BUF_SIZE = 1024;
/**
* 詳細ログ出力モード
*/
private static boolean verboseMode = false;
/**
* @param args
*/
public static void main(String[] args) {
verboseMode = true;
try {
if ("-c".equals(args[0])) {
// 圧縮モード
String[] targetFiles = new String[args.length - 2];
System.arraycopy(args, 2, targetFiles, 0, targetFiles.length);
deflate(new FileOutputStream(args[1]), targetFiles);
return;
} else if ("-x".equals(args[0])) {
// 展開モード
inflate(new FileInputStream(args[1]));
return;
}
} catch (Exception e) {
e.printStackTrace();
}
System.out
.println("[Usage] java com.snail.zip.ZipUtil -[cx] [zip file] [source files]");
}
/**
* ファイルをZIP形式で圧縮します.
*
* <pre>
* deflate(os, targetFileNames, Deflater.DEFAULT_COMPRESSION)
* と同じです
* </pre>
*
* @param os
* 圧縮結果を出力する OutputStream
* @param targetFileNames
* 圧縮するファイル名群
* @throws IOException
* 圧縮に失敗
*/
public static void deflate(OutputStream os, String[] targetFileNames)
throws IOException {
deflate(os, targetFileNames, Deflater.DEFAULT_COMPRESSION);
}
/**
* ファイルをZIP形式で圧縮します.
*
* @param os
* 圧縮結果を出力する OutputStream
* @param targetFileNames
* 圧縮するファイル名群
* @param leve
* 圧縮レベル(Deflater.BEST_SPEED(=0)〜Deflater.BEST_COMPRESSION(9))
* @throws IOException
* 圧縮に失敗
*/
public static void deflate(OutputStream os, String[] targetFileNames,
int level) throws IOException {
ZipOutputStream zipOut = new ZipOutputStream(new BufferedOutputStream(
os));
zipOut.setLevel(level);
// java.util.zip ではファイル名はUTF-8決めうち
// org.apache.tools.zip ではファイル名の文字コードを指定できる
// zipOut.setEncoding("Windows-31J");
for (int i = 0; i < targetFileNames.length; i++) {
File file = new File(targetFileNames[i]);
entryFile(zipOut, file, 0);
}
zipOut.close();
}
/**
* ファイルをZIP形式で展開します.
*
* @param is
* 展開内容を入力する InputStream
* @throws IOException
* 展開に失敗
*/
public static void inflate(InputStream is) throws IOException {
ZipInputStream zipInp = new ZipInputStream(new BufferedInputStream(is));
ZipEntry entry = null;
while ((entry = zipInp.getNextEntry()) != null) {
if (entry.isDirectory()) {
new File(entry.getName()).mkdirs();
} else {
new File(new File(entry.getName()).getParent()).mkdirs();
CheckedOutputStream out = new CheckedOutputStream(
new BufferedOutputStream(new FileOutputStream(entry
.getName())), new CRC32());
byte[] buf = new byte[BUF_SIZE];
int writeSize = 0;
int totalSize = 0;
while ((writeSize = zipInp.read(buf)) != -1) {
totalSize += writeSize;
out.write(buf, 0, writeSize);
}
out.close();
verbose(entry);
if (entry.getSize() != totalSize) {
verbose("格納サイズが違います");
}
if (entry.getCrc() != out.getChecksum().getValue()) {
verbose("チェックサムが違います");
}
}
zipInp.closeEntry();
}
zipInp.close();
}
/**
* ZIPにファイルを登録します.
*
* <pre>
* ZIPファイルにエントリを登録します。
* 登録しようとしているファイルがディレクトリの場合には、
* 再帰的に中身をエントリします。
* </pre>
*
* @param zip
* ZipOutputStream
* @param targetFile
* エントリするファイル
* @param depth
* 現在登録しようとしているディレクトリの深さ
* @throws IOException
* エントリの登録に失敗
*/
private static void entryFile(final ZipOutputStream zipOut,
final File targetFile, final int depth) throws IOException {
if (depth > MAX_DEPTH) {
throw new IOException("格納しようとしているディレクトリの構造が深すぎます。"
+ "シンボリックリンクによる循環参照が起きているのかもしれません。");
}
if (targetFile.isDirectory()) {
// エントリ内容はディレクトリ
File[] targetFiles = targetFile.listFiles();
if (targetFiles.length == 0) {
// エントリ内容は空のディレクトリ(ファイルパスの末尾を"/"にしてエントリ)
entry(zipOut, getFilePath(targetFile) + "/", null);
} else {
// エントリ内容が中身のあるディレクトリの場合には
// このディレクトリ自体はエントリせず再帰的に中身をエントリする
for (int i = 0; i < targetFiles.length; i++) {
entryFile(zipOut, targetFiles[i], depth + 1);
}
}
} else {
// エントリ内容はファイル
entry(zipOut, getFilePath(targetFile), new FileInputStream(
targetFile));
}
}
/**
* ZIPにデータを登録します.
*
* <pre>
* InputStreamから出力されるデータをZIPに登録します。
* このとき、CRC、SIZEも計算して登録します。
*
* InputStreamに、nullを設定するとディレクトリエントリと見なして
* データは登録しません。
* </pre>
*
* @param zipOut
* ZipOutputStream
* @param name
* エントリするファイル名
* @param entryIs
* エントリするデータを供給するInputStream
* @throws IOException
* エントリの登録に失敗
*/
private static void entry(final ZipOutputStream zipOut, final String name,
final InputStream entryIs) throws IOException {
ZipEntry entry = new ZipEntry(name);
zipOut.putNextEntry(entry);
int totalSize = 0;
if (entryIs != null) {
byte buf[] = new byte[BUF_SIZE];
int readSize;
CheckedInputStream in = new CheckedInputStream(
new BufferedInputStream(entryIs), new CRC32());
while ((readSize = in.read(buf, 0, BUF_SIZE)) != -1) {
totalSize += readSize;
zipOut.write(buf, 0, readSize);
}
in.close();
entry.setCrc(in.getChecksum().getValue());
}
entry.setSize(totalSize);
entry.setCompressedSize(totalSize); // Javadocによると此処にも圧縮解除時の容量を入れる
verbose(entry);
zipOut.closeEntry();
}
/**
* ファイルのパス名を得る.
*
* <pre>
* バックスラッシュ(\)をスラッシュ(/)に変更します
* </pre>
*
* @param file
* ファイル
* @return 引数ファイルのパス名
*/
private static String getFilePath(File file) {
String rootPath = root.getAbsolutePath();
String entryPath = file.getAbsolutePath();
String retPath;
// 基底ディレクトリがファイルと同じ場合 (ファイル名を指定された場合)
// にはファイル名を返す。
if (rootPath.equals(entryPath)) {
retPath = file.getName();
} else {
retPath = entryPath.replace(rootPath, "").replaceAll("\\\\", "/");
}
// Windows XP/Vista の標準の展開ツールは先頭が"/"だとファイル名として認識しないための対策
if (retPath.charAt(0) == '/') {
retPath = retPath.substring(1);
}
return retPath;
}
/**
* 詳細ログを出力します.
*
* <pre>
* static変数の verbose_mode が、true のときに詳細ログを出力します。
* </pre>
*
* @param obj
* 出力対象のオブジェクト
*/
private static void verbose(Object obj) {
if (verboseMode) {
if (obj instanceof ZipEntry) {
ZipEntry zipEntry = (ZipEntry) obj;
System.out.println(zipEntry.getName() + "\tSize:"
+ zipEntry.getSize() + "\tCRC32:" + zipEntry.getCrc());
}
System.out.println(obj);
}
}
}
C:\>java com.snail.zip.ZipUtil -c a.zip Infineon Infineon/0x0404.ini Size:3187 CRC32:3437384706 Infineon/0x0404.ini Infineon/0x0407.ini Size:5050 CRC32:4276019520 Infineon/0x0407.ini Infineon/0x0409.ini Size:4346 CRC32:21103272 Infineon/0x0409.ini Infineon/0x040a.ini Size:5101 CRC32:2468132550 Infineon/0x040a.ini Infineon/0x040c.ini Size:5262 CRC32:2174859066 ...
C:\temp>java com.snail.zip.ZipUtil -x ..\a.zip Infineon/0x0404.ini Size:3187 CRC32:3437384706 Infineon/0x0404.ini Infineon/0x0407.ini Size:5050 CRC32:4276019520 Infineon/0x0407.ini Infineon/0x0409.ini Size:4346 CRC32:21103272 Infineon/0x0409.ini Infineon/0x040a.ini Size:5101 CRC32:2468132550 Infineon/0x040a.ini Infineon/0x040c.ini Size:5262 CRC32:2174859066 ...
- import java.util.zip.ZipEntry; - import java.util.zip.ZipOutputStream; + import org.apache.tools.zip.ZipEntry; + import org.apache.tools.zip.ZipOutputStream;
ZipOutputStream#setEncoding("Windows-31J"); ' Here is a list of OEM code pages:
' 437 OEM - United States
' 737 OEM - Greek (formerly 437G)
' 775 OEM - Baltic
' 850 OEM - Multilingual Latin I
' 852 OEM - Latin II
' 855 OEM - Cyrillic (primarily Russian)
' 857 OEM - Turkish
' 858 OEM - Multlingual Latin I + Euro symbol
' 860 OEM - Portuguese
' 861 OEM - Icelandic
' 862 OEM - Hebrew
' 863 OEM - Canadian - French
' 864 OEM - Arabic
' 865 OEM - Nordic
' 866 OEM - Russian
' 869 OEM - Modern Greek
' 874 ANSI/OEM - Thai (same as 28605, ISO 8859-15)
' 932 ANSI/OEM - Japanese, Shift-JIS
' 936 ANSI/OEM - Simplified Chinese (PRC, Singapore)
' 949 ANSI/OEM - Korean (Unified Hangeul Code)
' 950 ANSI/OEM - Traditional Chinese (Taiwan; Hong Kong SAR, PRC)