web-dev-qa-db-fra.com

Comment forcer le navigateur à télécharger le fichier?

Tout fonctionne bien, mais si le fichier est petit, environ 1 Mo, lorsque je l'ai essayé avec des fichiers plus volumineux, comme par exemple 20 Mo, mon navigateur l'affiche au lieu de forcer le téléchargement. J'ai essayé plusieurs en-têtes jusqu'à présent.

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();
53
dieeying

Vous définissez les en-têtes de réponse après avoir écrit le contenu du fichier dans le flux de sortie. Il est assez tard dans le cycle de vie de la réponse pour définir des en-têtes. La séquence d'opérations correcte doit être de définir d'abord les en-têtes, puis d'écrire le contenu du fichier dans le flux de sortie du servlet.

Par conséquent, votre méthode devrait être écrite comme suit (cela ne compilera pas car c'est une simple représentation):

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");
}

La raison de l'échec est qu'il est possible que les en-têtes envoyés par le servlet soient différents de ceux que vous souhaitez envoyer. Après tout, si le conteneur de servlet ne sait pas quels en-têtes (qui apparaissent avant le corps de la réponse HTTP), il peut définir les en-têtes appropriés pour garantir la validité de la réponse. la définition des en-têtes après l'écriture du fichier est donc inutile et redondante car le conteneur a peut-être déjà défini les en-têtes. Vous pouvez le confirmer en consultant le trafic réseau à l'aide de Wireshark ou d'un proxy de débogage HTTP tel que Fiddler ou WebScarab.

Vous pouvez également vous référer à la Java EE API pour ServletResponse.setContentType pour comprendre ce comportement:

Définit le type de contenu de la réponse envoyée au client, si la réponse n'a pas encore été validée. Le type de contenu donné peut inclure une spécification de codage de caractères. Par exemple, text/html; charset = UTF-8. Le codage de caractères de la réponse est uniquement défini à partir du type de contenu donné si cette méthode est appelée avant l'appel de getWriter.

Cette méthode peut être appelée à plusieurs reprises pour modifier le type de contenu et le codage des caractères. Cette méthode n'a aucun effet si elle est appelée après la validation de la réponse.

...

65
Vineet Reynolds

Définissez le type de contenu et les autres en-têtes avant vous écrivez le fichier. Pour les petits fichiers, le contenu est mis en mémoire tampon et le navigateur obtient d'abord les en-têtes. Pour les gros, les données viennent en premier.

5
Vladimir Dyuzhev

Cela provient d'un script php qui résout parfaitement le problème avec tous les navigateurs que j'ai testés (FF depuis 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));

Donc, autant que je sache, vous faites tout correctement. Avez-vous vérifié les paramètres de votre navigateur?

4
f1sh