Я попытался прочитать InputStream из URL-адреса и напрямую написать в HttpServletResponse, не создавая временный файл в HttpServlet, но не смог получить реальную длину содержимого файла. Мне сказали, что я не могу определить количество данных в потоке, не прочитав их. Кто-нибудь может мне помочь? Я был бы очень признателен.
3 ответа
Поскольку вы читаете с URL, у вас должна быть длина, доступная из заголовка Content-length
этого URL.
HttpURLConnection connection = (HttpURLConnection)url.openConnection();
connection.connect();
if ( connection.getResponseCode() == 200 ) {
int contentLength = connection.getContentLength();
response.setContentLength( contentLength );
Обратите внимание, что вам не следует использовать inputStream.available()
. Метод available()
просто сообщает вам, сколько байтов можно прочитать без блокировки (то есть, ожидая прибытия следующего фрагмента из сети). Он не сообщает вам размер потока! Но в HttpURLConnection
вы можете получить значение заголовка длины содержимого с помощью метода getContentLength()
.
Как только вы это сделаете, вы можете запустить цикл чтения-записи для копирования всех данных, например
InputStream source = connection.getInputStream();
OutputStream target = response.getOutputStream();
int FILE_CHUNK_SIZE = 1024 * 4;
byte[] chunk = new byte[FILE_CHUNK_SIZE];
int n =0;
while ( (n = source.read(chunk)) != -1 ) {
target.write(chunk, 0, n);
}
Не забудьте использовать try-catch для всего этого, так как в такой транзакции может быть много видов исключений. И когда вы поймаете исключение, не забудьте указать в своем ответе код ответа, который не 200.
Я использую этот фрагмент кода, без управления длиной содержимого (IOUtils находится в commons-io и не создает временный файл), но работает с маленькими и большими файлами:
public static void downloadFile(HttpServletResponse response, InputStream inputStream, String fileName,
String contentType) throws IOException {
response.reset();
response.setHeader("Content-disposition", String.format("attachment; filename=\"%s\"",
fileName));
if (contentType != null) {
response.setHeader("Content-Type", contentType);
}
OutputStream output = response.getOutputStream();
IOUtils.copy(inputStream, output);
output.flush();
output.close();
}
Нет причин, по которым вы не можете прочитать входной поток из объекта запроса, пока в нем не закончатся данные - буферизуя их в памяти. На самом деле, по моему опыту, это всегда так; Я могу сосчитать на пальцах одной руки, сколько раз я видел, как входной поток записывался в файл вместо буферизации в памяти. Если у вас есть заголовок с размером содержимого, вы можете использовать его как отправную точку, но я бы не стал на него полагаться. Вам нужно будет защитить от слишком большого количества входных данных, потому что вы не знаете, что находится на дальнем конце входного потока, и поэтому они серверы, с которыми вы связываетесь, вы можете отправить так много материала, что у вас закончится ОЗУ для его обработки. .
Просто продолжайте вызывать read (), пока не получите -1. Для этого также можно использовать другие методы чтения, но вы добавите дополнительную логику для определения конца файла.
Похожие вопросы
Новые вопросы
java
Java — это высокоуровневый объектно-ориентированный язык программирования. Используйте этот тег, если у вас возникли проблемы с использованием или пониманием самого языка. Этот тег часто используется вместе с другими тегами для библиотек и/или фреймворков, используемых разработчиками Java.