java.util.zip.ZipOutputStream
による ZIP アーカイブについて調べてたら、以下のようなコードを見かけました。(適当に抜粋してます)
単純に、ディレクトリ配下のファイルを commons-io で取得して、ZIP にアーカイブするコードです。これ自体は正しく動作するし、目的通りに ZIP ファイルが生成されます。
しかし、呼び出し元が以下のような感じだったら。
archive(dirPath);
FileUtils.deleteDirectory(new File(dirPath));
要するに、ZIP ファイルを生成したあと、もとのディレクトリを削除するような場合。このとき、FileUtils#deleteDirectory
でエラーが発生します。
java.io.IOException: Unable to delete file: ... at org.apache.commons.io.FileUtils.forceDelete(FileUtils.java:2192) at org.apache.commons.io.FileUtils.cleanDirectory(FileUtils.java:1585) at org.apache.commons.io.FileUtils.deleteDirectory(FileUtils.java:1467) at org.apache.commons.io.FileUtils.forceDelete(FileUtils.java:2183) at org.apache.commons.io.FileUtils.cleanDirectory(FileUtils.java:1585) at org.apache.commons.io.FileUtils.deleteDirectory(FileUtils.java:1467) at org.apache.commons.io.FileUtils.forceDelete(FileUtils.java:2183) at org.apache.commons.io.FileUtils.cleanDirectory(FileUtils.java:1585) at org.apache.commons.io.FileUtils.deleteDirectory(FileUtils.java:1467) at org.apache.commons.io.FileUtils.forceDelete(FileUtils.java:2183) at org.apache.commons.io.FileUtils.cleanDirectory(FileUtils.java:1585) at org.apache.commons.io.FileUtils.deleteDirectory(FileUtils.java:1467) ...
冒頭のコードだと、writeZipEntry
メソッドに File オブジェクトの配列を渡して、同じ InputStream の参照でそれぞれのファイルを読み込んでいます。最後に finally で InputStream を close していますが、最後に処理したファイルのストリームしか close されていないのではないかと推測しています。そのため、後続のディレクトリの削除処理でエラーが発生しているのではないか、というわけです。(自信はない…)
冒頭のコードを以下のように変更すると、先のエラーは発生しなくなります。
要するに、処理するファイルごとに writeZipEntry
メソッドを呼び出して、毎回 InputStream を close するようにします。
java.io.InputStream
は複数のファイル読み込みで共有せず、ファイル読み込みごとに close すること。
余談
ちなみに冒頭のコードですが、writeZipEntry
メソッドの最後で System#gc
を呼び出すとエラーが発生しなくなります。うーむ…。