Я попытался прочитать InputStream из URL-адреса и напрямую написать в HttpServletResponse, не создавая временный файл в HttpServlet, но не смог получить реальную длину содержимого файла. Мне сказали, что я не могу определить количество данных в потоке, не прочитав их. Кто-нибудь может мне помочь? Я был бы очень признателен.

1
dddew 6 Сен 2016 в 16:16

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.

1
RealSkeptic 6 Сен 2016 в 15:17

Я использую этот фрагмент кода, без управления длиной содержимого (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();
}
-1
Juan Manuel Reina Morales 6 Сен 2016 в 14:01

Нет причин, по которым вы не можете прочитать входной поток из объекта запроса, пока в нем не закончатся данные - буферизуя их в памяти. На самом деле, по моему опыту, это всегда так; Я могу сосчитать на пальцах одной руки, сколько раз я видел, как входной поток записывался в файл вместо буферизации в памяти. Если у вас есть заголовок с размером содержимого, вы можете использовать его как отправную точку, но я бы не стал на него полагаться. Вам нужно будет защитить от слишком большого количества входных данных, потому что вы не знаете, что находится на дальнем конце входного потока, и поэтому они серверы, с которыми вы связываетесь, вы можете отправить так много материала, что у вас закончится ОЗУ для его обработки. .

Просто продолжайте вызывать read (), пока не получите -1. Для этого также можно использовать другие методы чтения, но вы добавите дополнительную логику для определения конца файла.

0
Jerry Andrews 6 Сен 2016 в 15:28