Zipの読み書きメモ

##はじめに
JavaにおけるZipファイルの読み書きのメモです。
ソースやテストに使用しているzipファイルは、
https://github.com/xaatw0/quiita/tree/master/src/ziptest
にあります。

##Streamの読み込み
 zip内のファイルを読み込んで、一つのファイルに連結する。

```java:zip関連.java
public void 連結_zip読み込み(InputStream zipFile, OutputStream output ) throws IOException{

    try(ZipInputStream zipIn = new ZipInputStream(zipFile,Charset.forName("SJIS"))
    	;BufferedOutputStream out = new BufferedOutputStream(output))
    {
		byte[] buffer = new byte[1024];

		ZipEntry entry = null;
		while(null != (entry = zipIn.getNextEntry())){

    			int size = 0;
    			while(0 < (size = zipIn.read(buffer))){
    				out.write(buffer, 0, size);
    			}
			zipIn.closeEntry();
		}
    }
}

/**
 * zip関連.zipには3つのファイルが入っている。エンコードはShiftJIS<br/>
 * 1.txt:あいうえお<br/>
 * 2.txt:かきくけこ(改行)<br/>
 * 3.txt:さしすせそ<br/>
 * 幸いzip内のファイルが名前の順番どおり呼ばれるのでちゃんと動くが、
 * 基本的には動作が保障されてないと思う。
 */
@Test
public void 連結_zip読み込みTest() throws IOException{

	ByteArrayOutputStream output = new ByteArrayOutputStream();
	連結_zip読み込み(getClass().getResourceAsStream("連結_zip読み込みTest.zip"), output);
	assertThat( new String(output.toByteArray(),"SJIS"), is("あいうえおかきくけこ\r\nさしすせそ"));
}
```

## zipの中身をReaderに変換して処理する
zip内のファイルに、日々の合計が「果物名:数」の形式で入っている。zip内のすべてのファイルを読み取り、果物ごとの合計を算出する。

```java:zip関連.java

public Map<String, Integer> zip読み込みして処理(InputStream inputStream) throws IOException{

	Map<String, Integer> result = new HashMap<>();

	try (ZipInputStream zipIn = new ZipInputStream(inputStream,Charset.forName("SJIS"))) {

		// zip内のファイルがなくなるまで読み続ける
		while (null != zipIn.getNextEntry()) {

			String line;
			BufferedReader reader = new BufferedReader(new InputStreamReader(zipIn, "Shift_JIS"));
			while(null != (line = reader.readLine())){

				String[] data = line.split(":");

				String fruit = data[0];
				int value = Integer.parseInt(data[1]);

				// すでに果物があれば加える。なければ、そのまま代入する。
				result.computeIfPresent(fruit, (k,v) -> v + value);
				result.putIfAbsent(fruit, value);
			}
			zipIn.closeEntry();
		}
	}

	return result;
}

/**
 * zip関連.zipには3つのファイルが入っている。エンコードはShiftJIS<br/>
 * 1.txt:りんご:3 みかん:1 バナナ:4<br/>
 * 2.txt:りんご:4 みかん:2<br/>
 * 3.txt:りんご:5 みかん:3 バナナ:1<br/>
 * 「りんご:3」で一行。
 */
@Test
public void zip読み込みして処理Test() throws IOException{

	Map<String, Integer> target =
			zip読み込みして処理(getClass().getResourceAsStream("zip読み込みして処理Test.zip"));

	// 果物は3種類
	assertThat(target.size(), is(3));

	assertThat(target.get("りんご"), is(12));
	assertThat(target.get("みかん"), is(6));
	assertThat(target.get("バナナ"), is(5));
}

```

## zipの書き出し
指定されたInputStreamをzipファイルに買い込む。ファイル名は「順番.txt」にする。

```java:zip関連.java
public void zip書き込み(OutputStream out, InputStream... input) throws IOException{

	try(ZipOutputStream zos = new ZipOutputStream(out)){
		int count = 0;

		for (InputStream stream: input){
			ZipEntry entry = new ZipEntry((count ++) + ".txt" );
			zos.putNextEntry(entry);

			try (InputStream is = new BufferedInputStream(stream)) {
				byte[] buf = new byte[1024];
				for (int len = 0; 0 < (len = is.read(buf))  ;) {
					zos.write(buf, 0, len);
				}
			}
		}
	}
}

/**
 * zipを書き込んで、zipを展開して内容を確認する。<br/>
 * ByteArrayOuputStreamで実施しているため、close処理が適当、、、
 */
@Test
public void zip書き込みTest() throws IOException{

	String[] data = {"あいうえお","かきくけこ","さしすせそ"};
	ByteArrayInputStream[] input = new  ByteArrayInputStream[data.length];

	for (int i = 0; i < data.length; i++){
		input[i] =  new ByteArrayInputStream(data[i].getBytes());
	}

	// Zipに書き込む(メモリー上に書き込む)
	ByteArrayOutputStream output = new ByteArrayOutputStream();
	zip書き込み(output, input);

	// 以下、zipを展開して、中身を確認する
	try(ZipInputStream zipIn = new ZipInputStream(new ByteArrayInputStream(output.toByteArray())))
    {
		byte[] buffer = new byte[1024];

		for (int i = 0; i < data.length ; i++){
			zipIn.getNextEntry();

			int size = 0;
			while(0 < (size = zipIn.read(buffer))){

				String zipString = new String(Arrays.copyOf(buffer, size));
				assertThat(zipString, is(data[i]));
			}
			zipIn.closeEntry();
		}
    }
}
```