Javaでgzipファイルの読み書き

# はじめに

そのままでサイズ大きいファイルを、gzip圧縮したままでやりとりするケースがあります。
javaでのgzipでのファイルの読み書きを整理しました。Java8のときからのものですが、java11でもいけそうです。

# 読み込み

csvファイルをgzip圧縮したものを例にします

ポイントは

0. try-with-resources構文を使う
1. InputSreamをGZIPInputStreamでラップする
2. GZIPInputStreamをInputStreamReaderでラップする
3. InputStreamReaderのときに、エンコーディングを指定できる
4. InputStreamReaderをBufferedReaderでラップする


```:Java

Path path = Paths.get("read_test.csv.gz");
try(
  InputStream is = Files.newInputStream(path);
  GZIPInputStream gis = new GZIPInputStream(is);
  InputStreamReader isReader = new InputStreamReader(gis, StandardCharsets.UTF_8);
  BufferedReader br = new BufferedReader(isReader); 
) {
  br.lines().forEach(System.out::println);
}

```

BufferedReaderでラップするのは性能のためです。
実践では、csv読み込みのライブラリを使うのが通例ですね。
ここでは[univocity_parsers](https://www.univocity.com/pages/univocity_parsers_tutorial.html) を使ったサンプルをあげてみます。

大容量を想定して、iteratorで取り扱う例です。

```
Path path = Paths.get("read_test.csv.gz");
try(
  InputStream is = Files.newInputStream(path);
  GZIPInputStream gis = new GZIPInputStream(is);
  InputStreamReader isReader = new InputStreamReader(gis, StandardCharsets.UTF_8);
  BufferedReader br = new BufferedReader(isReader); 
) {
  CsvParserSettings parserSettings = new CsvParserSettings();
  CsvRoutines routines = new CsvRoutines(parserSettings);
  Iterator<TestDTO> iterator = routines.iterate(TestDTO.class, br).iterator();
  iterator.forEachRemaining(x -> System.out.println(x.toString()));
}
```


# 書き込み

ポイントは読み込みとほぼ同様で

0. try-with-resources構文を使う
1. OutputStreamをGZIPOutputStreamでラップする
2. GZIPOutputStreamをOutputStreamWriterでラップする
3. OutputStreamWriterでエンコーディングを指定できる
4. OutputStreamWriterをBufferedWriterでラップする

```
Path path = Paths.get("write_test.csv.gz");
try (
  OutputStream os = Files.newOutputStream(path,StandardOpenOption.CREATE, StandardOpenOption.TRUNCATE_EXISTING, StandardOpenOption.WRITE);
  GZIPOutputStream gzip = new GZIPOutputStream(os);
  OutputStreamWriter ow = new OutputStreamWriter(gzip, StandardCharsets.UTF_8);
  BufferedWriter bw = new BufferedWriter(ow);) {
  List<String> rows = ...;
  rows.stream().forEach(row -> bw.write(row)); 
}  
```

csvライブラリを使っての書き込みの例はこうなります。

```
Path path = Paths.get("write_test.csv.gz");
try (
  OutputStream os = Files.newOutputStream(path,StandardOpenOption.CREATE, StandardOpenOption.TRUNCATE_EXISTING, StandardOpenOption.WRITE);
  GZIPOutputStream gzip = new GZIPOutputStream(os);
  OutputStreamWriter ow = new OutputStreamWriter(gzip, StandardCharsets.UTF_8);
  BufferedWriter bw = new BufferedWriter(ow);) {
  List<TestDTO> rows = ...;
  CsvWriterSettings writerSettings = new CsvWriterSettings();
  CsvWriter writer = new CsvWriter(bw , writerSettings);
  rows.stream().forEach(rows -> writer.processRecord(row));
}  
```

# 所感

やり方がわかればなんてことはないですが、一体、何回オブジェクトをラップすればいいんだ、という感じです。基本的に、csvライブラリにはreader or writerを渡せば、よしなにやってくれることが多いです。その点では、何度もラップした甲斐があるというものです。

# 参考リンク

* https://qiita.com/neko_the_shadow/items/dd4ce3f71388771c2bb7
* https://www.journaldev.com/966/java-gzip-example-compress-decompress-file
* http://commons.apache.org/proper/commons-compress/examples.html
* https://qiita.com/wakwak/items/49d5ffdb8372f45f4289
* https://docs.oracle.com/javase/jp/8/docs/api/java/io/BufferedReader.html
* https://qiita.com/ksby/items/f5b2f0f04e2ba590d270
* https://www.univocity.com/pages/univocity_parsers_tutorial.html
* https://www.univocity.com/pages/univocity_parsers_routines.html