play 2 アプリケーションを作成していて、ファイル ストリーミングの問題に苦しんでいます。次の署名を持つメソッドでサードパーティ API を使用してファイルを取得します。
FileMetadata getFile(OutputStream destination, String fileId)
従来のサーブレット アプリケーションでは、クライアントにコンテンツを送信したい場合、次のようにします。
HttpServletResponse resp;
myService.getFile(resp.getOutpuStream, fileId);
私の問題は、私のプレイ 2 コントローラー クラスでは、基になる OuputStream にアクセスできないため、コントローラー メソッドの最も単純な実装は次のようになることです。
public static downloadFile(String id) {
ByteArrayOutputStream baos = new BAOS(...);
myApi.getFile(baos,id); //Load inside temp Array
ByteArrayInputStream bais = new BAIS(baos.toByteArray())
return Ok(bais);
}
動作しますが、提供する前にコンテンツ全体をメモリにロードする必要があるため、オプションではありません (ファイルが巨大になる可能性があります)。
私は次のものからなる解決策を考えていました:
- コントローラー内で ByteArrayOutputStream (baos) を定義する
- この baos in パラメータを使用してサードパーティ API を呼び出す
- Play フレームワークのチャンク リターンを使用して、サードパーティ API によって内部に何かが書き込まれるとすぐにバオスのコンテンツを送信する
問題は、それが可能かどうか (getFile の呼び出しがブロックされているため、共有 OutputStream を持つ複数のスレッドが必要になるかどうか) も、やり過ぎかどうかもわかりません。
誰かがこの種の問題に直面し、解決策を見つけたことがありますか? 私が提案したソリューションは私の問題を解決できますか?
どんな洞察も高く評価されます。
ありがとう
EDIT 1 ケラウドの提案に基づいて、私はうまく機能するが、まだ完全ではない解決策を得ることができました(以下のコード)。
残念ながら、getFile メソッドの呼び出し中に問題が発生した場合、エラーはクライアントに返されず (私が Ok を返したため)、ブラウザーは決して来ないファイルを無期限に待機します。
このケースを処理する方法はありますか?
public static Result downloadFile(String fileId {
Thread readerThread = null;
try {
PipedOutputStream pos = new PipedOutputStream();
PipedInputStream pis = new PipedInputStream(pos);
//Reading must be done in another thread
readerThread = new DownloadFileWorker(fileId,pos);
readerThread.start();
return ok(pis);
} catch (Exception ex) {
ex.printStackTrace();
return internalServerError(ex.toString());
}
}
static class DownloadFileWorker extends Thread{
String fileId;
PipedOutputStream pos;
public DownloadFileWorker(String fileId, PipedOutputStream pos) {
super();
this.fileId = fileId
this.pos = pos;
}
public void run(){
try {
myApi.getFile(pos,fileId);
pos.close();
} catch (Exception ex) {
ex.printStackTrace();
}
}
}
編集2
ワーカー スレッドの catch() 部分に pos.close を追加するだけで、ページの無限ロードを回避する方法を見つけました。クライアントは最終的に 0 KB のファイルになりますが、無限に待機するよりはましだと思います。