Téléchargement de fichiers (servlet et portlet)

MonDossierWeb doit proposé de télécharger des fichier excel et pdf : trombinoscope d’une liste d’étudiants en pdf, une liste d’inscrits en excel, etc.

Ces fichiers doivent être générés ( par un ‘controller’ par exemple) au moment du téléchargement.

Au départ nous avions réussi à faire un téléchargement pour le fonctionnement en servlet. Il fonctionnait de la manière suivante :

//récupération de l’objet ‘HttpServletResponse’

FacesContext context = FacesContext.getCurrentInstance();

ExternalContext external = context.getExternalContext();

HttpServletResponse response = (HttpServletResponse) external.getResponse();

//formatage de la réponse

response.setContentType(« application/octet-stream »);

response.setHeader (« Content-Disposition », « attachment; filename=\ »test.xls\ » » );

ByteArrayOutputStream baos = new ByteArrayOutputStream(1024);

//création du fichier excel

HSSFWorkbook wb=creerExcel();

// Ecriture du fichier dans l’outputstream de la réponse

ServletOutputStream out;

try {

out = response.getOutputStream();

wb.write(baos);

baos.writeTo(out);

baos.flush();

//on ‘courcircuite’ le cycle de vie JSF :

context.responseComplete();

} catch (IOException e) {

e.printStackTrace();

}

Evidemment cette méthode de fonctionne pas en portlet car on ne peut récupérer l’objet ‘HttpServletResponse’. Il a donc fallu trouver une autre solution.

Il s’avère en fait que le téléchargement de fichiers placés dans la ‘response’ est impossible en portlet. La solution est donc de passer par une servlet. Nous avons l’avantage d’utiliser esup-commons, qui propose une telle servlet : « DownloadServlet ». Voici comment cela fonctionne en portlet :

//récupération de la session :

FacesContext context = FacesContext.getCurrentInstance();

ExternalContext external = context.getExternalContext();

ActionRequest request = (ActionRequest) external.getRequest();

PortletSession sessionp = request.getPortletSession();

try {

ByteArrayOutputStream baos = new ByteArrayOutputStream(1024);

//création du fichier excel:

HSSFWorkbook wb=creerExcel();

//récupération du fichier en tableau de bytes

wb.write(baos);

byte[] bytes=baos.toByteArray();

//on place le tableau de bytes dans la session :

sessionp.setAttribute(« downloadData », bytes, PortletSession.APPLICATION_SCOPE);

//on renseigne le ‘contenttype’ dans la session :

sessionp.setAttribute(« downloadContentType », « application/octet-stream », PortletSession.APPLICATION_SCOPE);

//on place le nom du fichier dans la session :

sessionp.setAttribute(« downloadFilename », « test.xls », PortletSession.APPLICATION_SCOPE);

//on redirige vers la servlet:

external.redirect(external.getRequestContextPath() + « /download »);

} catch (DownloadException e) {

e.printStackTrace();

} catch (IOException e) {

e.printStackTrace();

}

//on ‘courcircuite’ le cycle de vie JSF :

context.responseComplete();

Evidemment il faut auparavant déclarer la servlet dans le web.xml :

<servlet>

<servlet-name>Download Servlet</servlet-name>

<servlet-class>

org.esupportail.commons.web.servlet.DownloadServlet

</servlet-class>

</servlet>

<servlet-mapping>

<servlet-name>Download Servlet</servlet-name>

<url-pattern>/download</url-pattern>

</servlet-mapping>

Voici maintenant le fonctionnement de la servlet :

public void service(final ServletRequest servletRequest,

final ServletResponse servletResponse) throws ServletException {

HttpServletRequest request = (HttpServletRequest) servletRequest;

HttpServletResponse response = (HttpServletResponse) servletResponse;

try {

//récupération du contenttype

String contentType = (String) HttpUtils.getSessionAttribute(request, downloadContentType);

if (contentType != null) {

//formatage de la réponse

response.setContentType(contentType);

}

//recuperation du nom du fichier

String filename = (String) HttpUtils.getSessionAttribute(request, downloadFilename);

if (filename != null) {

//formatage de la réponse

response.setHeader(« Content-disposition », « inline; filename=\ » » + filename + « \ » »);

}

//récupération du fichier sous forme d’un tableau de bytes

byte [] data = (byte []) HttpUtils.getSessionAttribute(request, downloadData);

if (data == null) {

throw new DownloadException(« data is null, can not download »);

}

// Ecriture du fichier dans l’outputstream de la réponse

response.setContentLength(data.length);

ServletOutputStream out = response.getOutputStream();

out.write(data);

} catch (Exception e) {

Exception de = new DownloadException(e);

ExceptionUtils.catchException(getServletContext(), de, request);

throw new ServletException(de);

}

}

Vous remarquerez que cette servlet utilise la méthode getSessionAttribute(HttpServletRequest request, String name)

de la classe HttpUtils. Voici son fonctionnement :

public static Object getSessionDownloadAttribute(final HttpServletRequest request,

final String name) {


HttpSession session = request.getSession();
if (session == null) {
LOG.warn(« no session, can not get session attribute [ » + name + « ] »);
return null;
}

return session.getAttribute(name);

}

En fait, cette méthode va aller chercher l’attribut de session souhaité dans la request placée en paramètre.

Nous avons donc utilisé cette technique pour les deux types de contexte. Ainsi, le code décrit en début de Post pour le fonctionnement servlet devient :

//on récupère la session :

FacesContext context = FacesContext.getCurrentInstance();

ExternalContext external = context.getExternalContext();

HttpServletRequest request = (HttpServletRequest) external.getRequest();

HttpSession sessions = request.getSession();

try {

ByteArrayOutputStream baos = new ByteArrayOutputStream(1024);

//creation du fichier:

HSSFWorkbook wb=creerExcel();

//récupération du fichier en tableau de bytes

wb.write(baos);

byte[] bytes=baos.toByteArray();

//on place les informations nécessaire et le fichier en session:

sessions.setAttribute(« downloadData », bytes);

sessions.setAttribute(« downloadContentType », « application/octet-stream »);

sessions.setAttribute(« downloadFilename », « test.xls »);

//redirection vers la servlet:

external.redirect(external.getRequestContextPath() + « /download »);

} catch (DownloadException e) {

e.printStackTrace();

} catch (IOException e) {

e.printStackTrace();

}

//on ‘courcircuite’ le cycle de vie JSF :

context.responseComplete();

Et voila. Il ne reste qu’à déterminer le ‘context’ (servlet ou portlet) au moment du téléchargement pour exécuter le bon bout de code.

ATTENTION : pour que cela fonctionne en portlet il faut impérativement penser à indiquer :

emptySessionPath= »true » dans la déclaration du ‘Connector’ dans le server.xml de Tomcat. Ex:


<Connector port= »8080″ maxHttpHeaderSize= »8192″
maxThreads= »150″ minSpareThreads= »25″ maxSpareThreads= »75″
enableLookups= »false » redirectPort= »8443″ acceptCount= »100″
connectionTimeout= »20000″ disableUploadTimeout= »true »
emptySessionPath= »true » />

Sans cela la portlet et la servlet auront une Session différente et le code ne fonctionnera, générant probablement une erreur du type « can not download data is null » (voir code de la DownloadServlet) ou « prepare method was not called »

2 Réponses to “Téléchargement de fichiers (servlet et portlet)”

  1. tom Says:

    Cela marche bien dans une portlet, et ce sans passer par une servlet…
    No comment!

  2. fabian Says:

    salut,

    cette solution ne marche pas !!
    avant ke le servlet soit appelé ,je recoit une exception :

    ERROR
    Cause: java.lang.IllegalStateException: setRenderParameter cannot be called after redirect
    Message: setRenderParameter cannot be called after redirect
    StackTrace:
    java.lang.IllegalStateException: setRenderParameter cannot be called after redirect


Laisser un commentaire