#java #playframework #playframework-2.3
#java #playframework #playframework-2.3
Вопрос:
У меня есть две службы, первая frontend_service и вторая backend_service, и я получаю большой файл из backend_service и пытаюсь переслать пользователю через frontend_service, используя response.getBodyAsStream(), но это вызывает «java.lang.OutOfMemoryError: превышен лимит накладных расходов GC» во frontend_service.
код для backend_service:
`
public static Result downloadLargeFile(String filePath){
File file = new File(filePath);
InputStream inputStream = new FileInputStream(file);
return ok(inputStream);
}
`
код для frontend_service:
`
public static F.Promise<Result> downloadLargeFile(String filePath) {
//this will call backend_service downloadLargeFile method.
String backEndUrl = getBackEndUrl(filePath);
return getInputStream(backEndUrl);
}
`
`
public static Promise<Result> getInputStream(String url) {
return WS.url(url).get().map(
response -> {
InputStream inputStream = response.getBodyAsStream();
return ok(inputStream);
}
);
}
`
Я попробовал предложенное здесь решение, прочитав несколько байтов за раз из InputStream и создав tmp-файл во frontend_service и отправив tmp-файл в качестве выходных данных из frontend_service.
`
public static Promise<Result> getInputStream(String url) {
return WS.url(url).get().map(
response -> {
InputStream inputStream = null;
OutputStream outputStream = null;
try {
inputStream = response.getBodyAsStream();
//write input stream to tmp file
final File tmpFile = new File("/tmp/tmp.txt");
outputStream = new FileOutputStream(tmpFile);
int read = 0;
byte[] buffer = new byte[500];
while((read = inputStream.read(buffer)) != -1){
outputStream.write(buffer, 0 , read);
}
return ok(tmpFile);
} catch (IOException e) {
e.printStackTrace();
return badRequest();
} finally {
if (inputStream != null) {inputStream.close();}
if (outputStream != null) {outputStream.close();}
}
}
);
`
Приведенный выше код также выдает java.lang.Ошибка OutOfMemoryError. Я пытаюсь использовать файл объемом 1 ГБ.
Комментарии:
1.
reading few bytes at a time from inputStream
это единственное решение, о котором я могу думать. Можете ли вы опубликовать, что пошло не так с этим.2. Я отредактировал вопрос.
3. Я подозреваю
return ok(tmpFile);
, что это вызывает ошибку. Можете ли вы вернуть что-то еще, чтобы проверить теорию. (Может быть файл меньшего размера из файловой системы).4. Спасибо за предложение. Я пробовал
ok("test")
, но та же ошибка (java.lang. Ошибка OutOfMemoryError). Похоже, что InputStream ожидает хранения всех данных вместо перехода к следующему выполнению и предоставления данных OutputStream для записи.
Ответ №1:
У меня нет реализации «под рукой», поэтому я напишу алгоритм.
1. Play использует AsyncHttpClient
under the WS
. Вам нужно получить его или создать, как описано в https://www.playframework.com/documentation/2.3.x/JavaWS#Using-WSClient
2. Затем вам необходимо реализовать AsyncCompletionHandler
, как в описании класса https://static.javadoc.io/org.asynchttpclient/async-http-client/2.0.0/org/asynchttpclient/AsyncHttpClient.html
3. В onBodyPartReceived
методе AsyncCompletionHandler
класса вам нужно поместить часть тела в ответ на фрагментированное воспроизведение. Ответы с изменениями, описанные здесь: https://www.playframework.com/documentation/2.3.x/JavaStream#Chunked-responses
Постскриптум
Обсуждение аналогичного решения, но в противоположном направлении — потоковая загрузка в службу «backend» (Amazon) через службу «frontend» (play 2): https://groups.google.com/d/msg/asynchttpclient/EpNKLSG9ymM/BAGvwl0Wby8J