Мне нужен эффективный способ загрузки изображений с возможностью масштабирования
Я хочу снизить риск резких скачков памяти во время больших нагрузок. Поэтому изначально у меня был план не хранить все изображение в оперативной памяти, а использовать потоковую передачу.
Я хочу, чтобы изображение 1. проверялось на тип мема 2. нормализовалось до jpg + изменение размера 3. сохранялось в minio (любое хранилище s3)
Что я уже сделал
Я отключил функцию multipart, так как при этом изображение будет временно сохраняться на диске перед его обработкой и т. д., поэтому потоковая передача невозможна
private final JakartaServletFileUpload upload;
...
@PostMapping("/single")
ResponseEntity uploadSingleFile(HttpServletRequest request) throws IOException {
if (!JakartaServletFileUpload.isMultipartContent(request)) {
return ResponseEntity.badRequest().body("Not multipart");
}
FileItemInputIterator itemIterator = upload.getItemIterator(request);
while (itemIterator.hasNext()) {
FileItemInput next = itemIterator.next();
if (next.isFormField()) continue;
InputStream inputStream = next.getInputStream();
...
Я использую Tika для определения MemeType. Судя по тому, что я прочитал, Тика сохраняет небольшие файлы tmp во время процесса обнаружения. Но я думаю, что это не должно быть слишком большой проблемой для памяти, верно??? Или здесь уже Тике нужно будет перенести весь образ в оперативную память, чтобы создать TikaInputStream?
Теперь все перепуталось, если этого еще не было раньше. Я хочу изменить размер изображения и закодировать его (png или jpg) в jpg. Насколько я понимаю, сейчас нам нужно сохранить весь файл в памяти. Может при таком подходе даже в 2 раза....плохо..
Может быть, мое требование изменить размер изображения перед сохранением сделает невозможным обработку изображения в истинной потоковой передаче от получения до хранилища.
Если это так, я подумал о том, как я могу сократить количество одновременных запросов, и понял, что у меня большой пробел в обучении тому, как Spring работает под капюшон относительно буферизации и т. д. Поэтому одним из подходов было использование пула потоков для той части, где я изменяю размер изображения, чтобы в памяти могло храниться только ограниченное (контролируемое) количество изображений. Но что произойдет, если пул потоков превысит свои пределы? Будет ли Spring сдерживать получение дальнейших TCP-пакетов, не подтверждая их, или Spring просто создает буферы внутренней памяти, поэтому в моей оперативной памяти будет «затор данных». Спасибо за разъяснения или ссылки на учебные ресурсы здесь.
Решите мою проблему
Каковы ваши стратегии для масштабируемой загрузки изображений? Боюсь ли я на данный момент необоснованно Multipart?
Как устранить пробел в знаниях относительно управления параллелизмом в Spring, например, как сдерживать множество запросов к определенным конечным точкам, как насыщение пула потоков влияет на Spring, его обработку памяти и запрос ACK?
Когда я прерываю потоковую передачу Java, загружая все данные в ОЗУ? Что можно «перекачивать», а что нельзя «перекачивать»?
[b]Требование[/b] [list] [*]Мне нужен эффективный способ загрузки изображений с возможностью масштабирования
[*]Я хочу снизить риск резких скачков памяти во время больших нагрузок. Поэтому изначально у меня был план не хранить все изображение в оперативной памяти, а использовать потоковую передачу.
[*]Я хочу, чтобы изображение 1. проверялось на тип мема 2. нормализовалось до jpg + изменение размера 3. сохранялось в minio (любое хранилище s3)
[/list] [b]Что я уже сделал[/b] [list] [*]Я отключил функцию multipart, так как при этом изображение будет временно сохраняться на диске перед его обработкой и т. д., поэтому потоковая передача невозможна [code]spring.servlet.multipart.enabled=false [/code]
[*]Я использую JakartaServletFileUpload, чтобы разрешить потоковую передачу изображений
[/list] [code]private final JakartaServletFileUpload upload; ... @PostMapping("/single") ResponseEntity uploadSingleFile(HttpServletRequest request) throws IOException { if (!JakartaServletFileUpload.isMultipartContent(request)) { return ResponseEntity.badRequest().body("Not multipart"); }
FileItemInputIterator itemIterator = upload.getItemIterator(request); while (itemIterator.hasNext()) { FileItemInput next = itemIterator.next(); if (next.isFormField()) continue; InputStream inputStream = next.getInputStream();
... [/code] [list] [*]Я использую Tika для определения MemeType. Судя по тому, что я прочитал, Тика сохраняет небольшие файлы tmp во время процесса обнаружения. Но я думаю, что это не должно быть слишком большой проблемой для памяти, верно??? Или здесь уже Тике нужно будет перенести весь образ в оперативную память, чтобы создать TikaInputStream? [/list] [code]TikaInputStream tikaInputStream = TikaInputStream.get(inputStream); String detectedType = tika.detect(tikaInputStream); [/code] [list] [*]Теперь все перепуталось, если этого еще не было раньше. Я хочу изменить размер изображения и закодировать его (png или jpg) в jpg. Насколько я понимаю, сейчас нам нужно сохранить весь файл в памяти. Может при таком подходе даже в 2 раза....плохо.. [/list] [code]try (ByteArrayOutputStream outputStream = new ByteArrayOutputStream()) { Thumbnails.of(context.getImageUploadStream()) .keepAspectRatio(true) .width(IMAGE_WIDTH) .outputFormat("jpg") .toOutputStream(outputStream); context.setImageUploadStream(new ByteArrayInputStream(outputStream.toByteArray())); } catch (IOException e) { throw new ImageUploadException(List.of(UNKNOWN_ERROR), e); } [/code] [b]Моя новая стратегия[/b] [list] [*]Может быть, мое требование изменить размер изображения перед сохранением сделает невозможным обработку изображения в истинной потоковой передаче от получения до хранилища.
[*]Если это так, я подумал о том, как я могу сократить количество одновременных запросов, и понял, что у меня большой пробел в обучении тому, как Spring работает под капюшон относительно буферизации и т. д. Поэтому одним из подходов было использование пула потоков для той части, где я изменяю размер изображения, чтобы в памяти могло храниться только ограниченное (контролируемое) количество изображений. Но что произойдет, если пул потоков превысит свои пределы? Будет ли Spring сдерживать получение дальнейших TCP-пакетов, не подтверждая их, или Spring просто создает буферы внутренней памяти, поэтому в моей оперативной памяти будет «затор данных». Спасибо за разъяснения или ссылки на учебные ресурсы здесь.
[/list] [b]Решите мою проблему[/b] [list] [*]Каковы ваши стратегии для масштабируемой загрузки изображений? Боюсь ли я на данный момент необоснованно Multipart?
[*]Как устранить пробел в знаниях относительно управления параллелизмом в Spring, например, как сдерживать множество запросов к определенным конечным точкам, как насыщение пула потоков влияет на Spring, его обработку памяти и запрос ACK?
[*]Когда я прерываю потоковую передачу Java, загружая все данные в ОЗУ? Что можно «перекачивать», а что нельзя «перекачивать»?