#java #file #httpclient #nio #segment
#java #файл #httpclient #nio #сегмент
Вопрос:
Я хочу загрузить огромный файл, который уже разделен на сотни сегментов, в один файл.
Итак, у меня есть все URL-адреса всех сегментов в массиве, я делю его вручную на 4 раздела, запускаю 4 потока одновременно, а затем объединяю их в конце в один огромный файл. Часть, в которой, я думаю, я мог бы улучшить свой код, находится в части, когда я загружаю разделы. Цель состоит в том, чтобы открыть файлы один раз и добавить в них все сегменты. Я открыт и для совершенно других решений.
static void downloadFileSection(int begin, int end, int marker, String[] segmentURLs, String segID) {
try(var file = FileChannel.open(Path.of(segID "_" marker ".seg"), WRITE, CREATE)){
var byteArrayHandler = BodyHandlers.ofByteArray();
IntStream.range(begin, end)
.forEach(index -> {
System.out.println("Downloading segment: " index);
try {
//This is the part that I think is wrong. I shouldn't be creating a ByteBuffer every segment download.
//Main.sendNormalRequest is a method that sends a request using java 11-s HttpClient.send method.
file.write(ByteBuffer.wrap(Main.sendNormalRequest(segmentURLs[index], byteArrayHandler).body()));
} catch (IOException e) {
e.printStackTrace();
}
});
}catch (IOException e) {
e.printStackTrace();
}
}
Комментарии:
1. Неясно, в чем заключается ваш вопрос. Вы хотите минимизировать использование памяти или хотите распараллелить процесс?
2. Я хочу знать, есть ли лучший способ загрузить много небольших файлов в один, в основном добавив все байты. Говоря об использовании памяти, я заметил, что анализатор памяти eclipse упоминает, что HttpClient хранит ссылки на байт-буферы.
3. «Самым быстрым» методом было бы выполнить несколько параллельных вызовов небольших файлов (например, поставить их в очередь через
ThreadPoolExecutor
), а затем записать части в целевой файл через одинRandomAccessFile
, скопировав входной поток из HTTP-соединения в RAF-OutputStream. Это также должно иметь наименьший объем памяти, потому что вы не загружаете файлы полностью в ОЗУ.4. HttpClient использует разные стратегии в зависимости от того, проходит ли запрос через HTTPS или обычный HTTP. В общем случае он может пересылать фрагменты буфера вызывающему коду, чтобы оптимизировать использование памяти, что может выглядеть так, как будто он содержит ссылку на байтовый буфер, в то время как он может просто ждать, пока следующие байты заполнят следующий фрагмент. С HTTPS тогда есть временные байтовые буферы, которые используются между сокетом и механизмом SSL: они обычно перерабатываются, и HttpClient сохранит ссылку на них, поскольку они никогда не переходят к вызывающему, но их должно быть всего несколько.
Ответ №1:
Вы можете использовать BodyHandlers.ofPublisher() для получения Publisher<List<ByteBuffer>>
. Затем, если статус 200 OK, предоставьте и подпишите свою собственную Flow.Subscriber<List<ByteBuffer>>
реализацию и запишите байты в файл по мере их поступления в onNext(...)
метод. Если эти байты представляют текст, вам, возможно, придется позаботиться о кодировке / перекодировании символов, особенно если вам нужно записать файл в кодировке, отличной от той, что отправляется сервером (обычно это будет UTF-8).