2012年4月4日水曜日

Playframeworkでダウンロード後にファイル削除

playframeworkでバイナリのダウンロード機能を実装する際にはrenderBinaryを使用する。
この時にダウンロード完了したファイルを削除しようとしていくつか試行錯誤が必要だったのでメモとして残しておく。
やったのは次の順

1、普通にメソッド内のfinallyで削除

public static void download(...)  throws IOException {
    File file = getDownloadFile(...);
    String filename = ...;
    try {
        renderBinary(file, filename);
    } finally {
        file.delete();
    }
}

結果: Fileが見つからない
何故ならrenderXXXXは対応するExcetionをthrowしてそのcatch節で処理を行うから。
catch節での処理が始まるときにはすでにdownloadメソッド自体を抜けており、そのfinally節でのファイル削除が実行されてしまっている。

2、@FinallyとThreadLocalの合わせ技


private static ThreadLocal<File> deleteFile;

@Finally
static void doDeleteFile() {
    File f = deleteFile.get();
    if (f != null) {
         f.delete();
         deleteFile.remove();
    }
}

public static void download(...)  throws IOException {
    File file = getDownloadFile(...);
    String filename = ...;

    deleteFile.set(file);

    renderBinary(file, filename);
}

結果:responseのcontent-lengthが0になる
@FinallyはrenderXXの処理実行後に実行されるので一見うまくいきそうに見える。
しかしRenderBinaryでレスポンスへのwriteが遅延処理されており、やっぱりwriteの前にファイルが削除されてしまう。

3、FileInputStreamのclose時に削除
結局うまくいったのはFileInputStreamのサブクラスを作成してそのclose時にファイルを削除するという方法

class FileInputStreamWithDelete extends FileInputStream {
    
    private File file;
    
    public FileInputStreamWithDelete(File file) throws FileNotFoundException {
        super(file);
        this.file = file;
    }
    
    @Override
    public void close() throws IOException {
        super.close();
        this.file.delete();
    }
}

public static void download(...) throws IOException {
    File file = getDownloadFile(...);
    String filename = ...;
    renderBinary(new FileInputStreamWithDelete(file), filename, file.length());
}

ちょっとひねりが必要な感じですね。

0 件のコメント:

コメントを投稿