Могу ли я записать несколько байтовых массивов в HttpClient без буферизации на стороне клиента? - PullRequest
2 голосов
/ 02 февраля 2012

Проблема

Я хотел бы загрузить очень большие файлы (до 5 или 6 ГБ) на веб-сервер, используя класс HttpClient (4.1.2) из ​​Apache.Перед отправкой этих файлов я разбиваю их на более мелкие куски (например, 100 МБ).К сожалению, все примеры, которые я вижу для выполнения POST из нескольких частей с использованием HttpClient, по-видимому, буферизуют содержимое файла перед отправкой (обычно предполагается небольшой размер файла).Вот такой пример:

HttpClient httpclient = new DefaultHttpClient();
HttpPost post = new HttpPost("http://www.example.com/upload.php");

MultipartEntity mpe = new MultipartEntity();

// Here are some plain-text fields as a part of our multi-part upload
mpe.addPart("chunkIndex", new StringBody(Integer.toString(chunkIndex)));
mpe.addPart("fileName", new StringBody(somefile.getName()));

// Now for a file to include; looks like we're including the whole thing!
FileBody bin = new FileBody(new File("/path/to/myfile.bin"));
mpe.addPart("myFile", bin);

post.setEntity(mpe);
HttpResponse response = httpclient.execute(post);

В этом примере похоже, что мы создаем новый объект FileBody и добавляем его к MultipartEntity.В моем случае, когда размер файла мог составлять 100 МБ, я бы предпочел не буферизовать все эти данные сразу.Я хотел бы иметь возможность записывать эти данные небольшими порциями (например, 4 МБ за раз), в конечном итоге записывая все 100 МБ.Я могу сделать это, используя класс HTTPURLConnection из Java (путем записи непосредственно в выходной поток), но у этого класса есть свой собственный набор проблем, поэтому я пытаюсь использовать предложения Apache.

Мой вопрос

Можно ли записать 100 МБ данных в HttpClient, но небольшими итеративными блоками?Я не хочу, чтобы клиенту пришлось буферизовать до 100 МБ данных перед тем, как делать POST.Кажется, что ни один из примеров, которые я вижу, не позволяет вам писать напрямую в выходной поток;все они, кажется, предварительно упаковывают вещи перед execute() вызовом.

Любые советы будут оценены!

--- Обновление ---

Для пояснения, вотчто я делал ранее с классом HTTPURLConnection.Я пытаюсь понять, как сделать нечто подобное в HttpClient:

// Get the connection's output stream
out = new DataOutputStream(conn.getOutputStream());

// Write some plain-text multi-part data
out.writeBytes(fieldBuffer.toString());

// Figure out how many loops we'll need to write the 100 MB chunk
int bufferLoops = (dataLength + (bufferSize - 1)) / bufferSize;

// Open the local file (~5 GB in size) to read the data chunk (100 MB)
raf = new RandomAccessFile(file, "r");
raf.seek(startingOffset); // Position the pointer to the beginning of the chunk

// Keep track of how many bytes we have left to read for this chunk
int bytesLeftToRead = dataLength;

// Write the file data block to the output stream
for(int i=0; i<bufferLoops; i++)
{
    // Create an appropriately sized mini-buffer (max 4 MB) for the pieces
    // of this chunk we have yet to read
    byte[] buffer = (bytesLeftToRead < bufferSize) ? 
                    new byte[bytesLeftToRead] : new byte[bufferSize];

    int bytes_read = raf.read(buffer); // Read ~4 MB from the local file
    out.write(buffer, 0, bytes_read); // Write that bit to the stream
    bytesLeftToRead -= bytes_read;
}

// Write the final boundary
out.writeBytes(finalBoundary);
out.flush();

Ответы [ 3 ]

0 голосов
/ 02 февраля 2012

Потоки - определенно лучший способ, я помню, что делал нечто подобное некоторое время назад с некоторыми большими файлами, и это работало отлично.

0 голосов
/ 02 февраля 2012

Все, что вам нужно, - это включить вашу собственную логику генерации контента в реализацию HttpEntity.Это даст вам полный контроль над процессом генерации и потоковой передачи контента.

И для записи: MultipartEntity, поставляемый с HttpClient, не буферизует части файла в памяти перед записью их всоединительная розетка.

0 голосов
/ 02 февраля 2012

Если я правильно понимаю ваш вопрос, ваша задача - загрузить весь файл в память (верно?). Если это так, вы должны использовать Streams (например, FileInputStream). Таким образом, весь файл не помещается в память сразу.

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

Лично я предпочитаю свой первый ответ, но в любом случае (или в любом случае, если это не поможет), Удачи!

...