Как заставить браузер загружать файл? - PullRequest
51 голосов
/ 29 июня 2011

Все работает нормально, но только если файл маленький, около 1 МБ, когда я попробовал его с большими файлами, например, 20 МБ, мой браузер отображает его, вместо того, чтобы принудительно загружать, я пробовал много заголовков до сих пор, теперь мой код выглядит так:

PrintWriter out = response.getWriter();
String fileName = request.getParameter("filename");

File f= new File(fileName);

InputStream in = new FileInputStream(f);
BufferedInputStream bin = new BufferedInputStream(in);
DataInputStream din = new DataInputStream(bin);

while(din.available() > 0){
    out.print(din.readLine());
    out.print("\n");
}

response.setContentType("application/force-download");
response.setContentLength((int)f.length());
response.setHeader("Content-Transfer-Encoding", "binary");
response.setHeader("Content-Disposition","attachment; filename=\"" + "xxx\"");//fileName);


in.close();
bin.close();
din.close();

Ответы [ 3 ]

65 голосов
/ 29 июня 2011

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

Следовательно, ваш метод должен быть написан следующим образом (это не скомпилируется, поскольку это просто представление):

response.setContentType("application/force-download");
response.setContentLength((int)f.length());
        //response.setContentLength(-1);
response.setHeader("Content-Transfer-Encoding", "binary");
response.setHeader("Content-Disposition","attachment; filename=\"" + "xxx\"");//fileName);
...
...
File f= new File(fileName);

InputStream in = new FileInputStream(f);
BufferedInputStream bin = new BufferedInputStream(in);
DataInputStream din = new DataInputStream(bin);

while(din.available() > 0){
    out.print(din.readLine());
    out.print("\n");
}

Причина сбоя заключается в том, что фактические заголовки, отправленные сервлетом, могут отличаться от того, что вы собираетесь отправлять. В конце концов, если сервлет-контейнер не знает, какие заголовки (которые появляются перед телом в HTTP-ответе), он может установить соответствующие заголовки, чтобы убедиться, что ответ действителен; поэтому установка заголовков после записи файла бесполезна и избыточна, поскольку контейнер, возможно, уже установил заголовки. Вы можете убедиться в этом, посмотрев на сетевой трафик с помощью Wireshark или прокси-сервера отладки HTTP, такого как Fiddler или WebScarab.

Вы также можете обратиться к документации по API Java EE для ServletResponse.setContentType , чтобы понять это поведение:

Устанавливает тип содержимого ответа, отправляемого клиенту, , если ответ еще не зафиксирован. Данный тип содержимого может включать спецификацию кодировки символов, например, text / html; charset = UTF-8. Кодировка символов ответа устанавливается только из данного типа содержимого, если этот метод вызывается до вызова getWriter.

Этот метод может вызываться повторно для изменения типа содержимого и кодировки символов. Этот метод не действует, если вызывается после подтверждения ответа.

...

4 голосов
/ 29 июня 2011

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

4 голосов
/ 29 июня 2011

Это от php-скрипта, который прекрасно решает проблему с каждым протестированным браузером (FF начиная с 3.5, IE8 +, Chrome)

header("Content-Disposition: attachment; filename=\"".$fname_local."\"");
header("Content-Type: application/force-download");
header("Content-Transfer-Encoding: binary");
header("Content-Length: ".filesize($fname));

Итак, насколько я вижу, вы все делаете правильно. Вы проверили настройки своего браузера?

...