Удобный для памяти способ записи InputStream в файл - PullRequest
0 голосов
/ 12 марта 2020

Я пытаюсь написать пакетный загрузчик для изображений. Получить InputStream из URLConnection достаточно просто, но загрузка всех файлов занимает некоторое время. Использование многопоточности, безусловно, ускоряет его, но загрузка потоков может занять много памяти. Вот что я нашел:

Пусть in будет InputStream, file целью File и fos a FileOutputStream до file

Простой способ

fos.write(in.readAllBytes());

Прочитать весь файл, написать возвращаемый byte[]. Вероятно, пригодится для получения исходного кода сайта, не годится для больших файлов, таких как изображения.

Запись фрагментов

 byte[] buffer = new byte[bufsize];
 int read;
 while ((read = in.read(buffer, 0, bufsize)) >= 0) {
     fos.write(buffer, 0, read);
 }

Мне кажется, лучше.

in.transferTo (fos)

in.transferTo(fos);

Внутренняя запись фрагментов, как показано выше.

Files.copy ()

Files.copy(in, file.toPath(),  StandardCopyOption.REPLACE_EXISTING);

Появляется использование собственных реализаций.

Какой из них следует использовать, чтобы минимизировать использование памяти, когда выполняются десятки раз параллельно?

Это небольшой забавный проект, внешние библиотеки для этого IMO излишни. Также я не могу использовать ImageIO, так как он не может обрабатывать webms, некоторые pngs / jpgs и анимированные GIF-файлы.

EDIT:
Этот вопрос был основан на предположении, что одновременная запись возможна. Тем не менее, это не так. Я, вероятно, получу ссылки на изображения одновременно, а затем буду загружать их одну за другой. В любом случае, спасибо за ответы!

Ответы [ 2 ]

1 голос
/ 12 марта 2020

Краткий ответ: с точки зрения использования памяти, лучшим решением является использование версии, которая считывает и хранит данные в блоках.

Размер буфера должен быть в основном выбран с учетом количества одновременных загрузок Доступная память, скорость загрузки и эффективность целевого диска с точки зрения скорости передачи данных и IOPS.

Длинный ответ заключается в том, что одновременная загрузка файлов не обязательно означает, что загрузка будет быстрее. Количество одновременных загрузок для фактического ускорения общего времени загрузки в основном зависит от:

  • количества хостов, с которых вы загружаете
  • скорость соединения inte rnet соединения хост, с которого вы скачиваете, ограничен скоростью сетевого адаптера этого хоста
  • скорость вашего inte rnet соединения, ограничена скоростью сетевого адаптера этого хоста
  • IOps хранилища хоста, с которого вы скачиваете
  • IOps хранилища, которое вы скачиваете в
  • Скорость передачи хранилища на хосте, с которого вы скачиваете
  • Скорость передачи загружаемого вами хранилища в
  • Производительность локальных и удаленных хостов. Например, некоторые старые или недорогие устройства android могут быть ограничены скоростью процессора.

Например, может показаться, что если на исходном хосте один жесткий диск и одно соединение уже дает полное соединение скорость, то использовать несколько подключений бесполезно, так как это замедлит загрузку, создавая издержки на переключение между передаваемыми файлами.

Также может быть, что исходный хост имеет ограничение скорости для одного подключения, поэтому несколько соединений могут ускорить процесс.

Жесткий диск обычно имеет значение IOPS около 80 IOPS и скорость передачи около 80 МБ / с, и это может ограничить скорость загрузки / выгрузки этими факторами. Таким образом, практически невозможно записать или прочитать с такого диска более 80 файлов в секунду, а скорость передачи превышает 80 МБ / с, что, конечно, вряд ли зависит от модели диска.

Дисковод SSD обычно имеет десятки тысяч операций ввода-вывода в секунду и скорость передачи данных> 400 МБ / с, поэтому ограничения намного больше, но для действительно быстрых соединений rnet они по-прежнему важны.

0 голосов
/ 12 марта 2020

Я нашел в inte rnet сравнение по времени (отсюда и производительность) здесь journaldev.com/861/java-copy-file

Однако, если вы сосредоточены на памяти, вы можете попробовать чтобы измерить потребление памяти самостоятельно, используя что-то вроде кода, предложенного @ pasha701 здесь

Runtime runtime = Runtime.getRuntime();
long usedMemoryBefore = runtime.totalMemory() - runtime.freeMemory();
System.out.println("Used Memory before" + usedMemoryBefore);
// copy file method here
long usedMemoryAfter = runtime.totalMemory() - runtime.freeMemory();
System.out.println("Memory increased:" + (usedMemoryAfter-usedMemoryBefore));

Обратите внимание, что возвращаемые значения приведены в байтах, разделите их на 1000000, чтобы получить значения в МБ.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...