Я работаю над приложением Spring-MVC, в котором пользователи могут загружать файлы. Пользователи могут нажать на приложение, которое запускает механизм загрузки.Spring, Java: загрузка потокового файла для устранения ошибок в памяти
Вчера, когда несколько загрузок и два из которых имели приблизительно 2 ГБ файлов, это вызвало ошибку из памяти (журнал ниже).
Чтобы избежать этой проблемы, один из способов решения этой проблемы представлял собой потоковое скачивание данных в кусках и обработку этих фрагментов только на уровне сервиса, а не на весь файл.
К сожалению, я не знаю, как двигаться дальше, любая помощь будет приятной. Если эта опция не может летать, вы можете найти рекомендации по решению этой проблемы.
Журнал ошибок: Код
HTTP Status 500 - Handler processing failed; nested exception is java.lang.OutOfMemoryError: Direct buffer memory
type Exception report
message Handler processing failed; nested exception is java.lang.OutOfMemoryError: Direct buffer memory
description The server encountered an internal error that prevented it from fulfilling this request.
exception
org.springframework.web.util.NestedServletException: Handler processing failed; nested exception is java.lang.OutOfMemoryError: Direct buffer memory
org.springframework.web.servlet.DispatcherServlet.triggerAfterCompletionWithError(DispatcherServlet.java:1303)
org.springframework.web.servlet.DispatcherServlet.doDispatch(DispatcherServlet.java:977)
org.springframework.web.servlet.DispatcherServlet.doService(DispatcherServlet.java:893)
org.springframework.web.servlet.FrameworkServlet.processRequest(FrameworkServlet.java:967)
org.springframework.web.servlet.FrameworkServlet.doGet(FrameworkServlet.java:858)
javax.servlet.http.HttpServlet.service(HttpServlet.java:620)
org.springframework.web.servlet.FrameworkServlet.service(FrameworkServlet.java:843)
Контроллер:
@RequestMapping(value = "/download/attachment/{attachid}", method = RequestMethod.GET)
public void getAttachmentFromDatabase(@PathVariable("attachid") int attachid,
, HttpServletResponse response,) {
response.setContentType("application/octet-stream");
GroupAttachments groupAttachments = this.groupAttachmentsService.getAttachmenById(attachid);
response.setHeader("Content-Disposition", "attachment; filename=\"" + groupAttachments.getFileName() + "\"");
response.setContentLength(groupAttachments.getSendAttachment().length);
FileCopyUtils.copy(groupAttachments.getSendAttachment(), response.getOutputStream());
response.flushBuffer();
}
Сервис слой:
@Override
public GroupAttachments getAttachmenById(int attachId) {
Person person = this.personService.getCurrentlyAuthenticatedUser();
GroupAttachments groupAttachments = this.groupAttachmentsDAO.getAttachmenById(attachId);
GroupMembers groupMembers = this.groupMembersService.returnMembersMatchingUsernameAccountId(person.getUsername(),
groupAttachments.getGroupId());
if (!(groupMembers == null)) {
if (person.getUsername().equals(groupMembers.getMemberUsername())) {
try {
Path path = Paths.get(msg + groupAttachments.getGroupId() + "/" +
groupAttachments.getFileIdentifier());
groupAttachments.setSendAttachment(Files.readAllBytes(path));
return groupAttachments;
} catch (IOException ignored) {
this.groupAttachmentsDAO.removeAttachment(attachId);
return null;
}
}
return null;
} else {
return null;
}
}
Спасибо. :-)
Update
Новый механизм загрузки:
Контроллер:
public ResponseEntity<byte[]> getAttachmentFromDatabase(@PathVariable("attachid") int attachid,
@PathVariable("groupaccountid") Long groupAccountId, @PathVariable("api") String api,
HttpServletResponse response,
@PathVariable("type") boolean type) {
Path path = this.groupAttachmentsService.getAttachmentPathById(attachid);
GroupAttachments groupAttachments = this.groupAttachmentsService.getAttachmentObjectOnlyById(attachid);
response.setContentType("application/octet-stream");
response.setHeader("Content-Disposition", "attachment; filename=\""+groupAttachments.getFileName()+"\"");
try {
OutputStream outputStream = response.getOutputStream();
Files.copy(path,outputStream);
outputStream.flush();
outputStream.close();
response.flushBuffer();
}
Сервис слой:
@Override
public Path getAttachmentPathById(int attachId){
Person person = this.personService.getCurrentlyAuthenticatedUser();
GroupAttachments groupAttachments = this.groupAttachmentsDAO.getAttachmenById(attachId);
GroupMembers groupMembers = this.groupMembersService.returnMembersMatchingUsernameAccountId(person.getUsername(),
groupAttachments.getGroupId());
if (!(groupMembers == null)) {
if (person.getUsername().equals(groupMembers.getMemberUsername())) {
try {
return Paths.get(msg + groupAttachments.getGroupId() + "/" +
groupAttachments.getFileIdentifier());
} catch (Exception ignored) {
return null;
}
}
return null;
} else {
return null;
}
}
Вы все читаете в памяти, не делайте этого ... Файлы Files.readAllBytes загружают весь «2Gb» в память. Просто не надо. Завершите загрузку и вместо первой загрузки, прочитайте ее часть по частям и поместите ее прямо. Вместо этого используйте 'Files.copy (Path, OutputStream)'. –
Копия будет передавать его, а не загружать. Пожалуйста, прочитайте комментарий ... –
@ M.Deinum: Извините за неправильное чтение. Я обновил код, а также попытался с Files.copy (путь, outputStream), работает красиво. Я опубликовал код как обновление в главном сообщении, можете ли вы проверить, есть ли какая-либо возможная оптимизация. Спасибо. –