Cargar archivos en una aplicación web usando JSF 2.2 Java EE 7

Por Diego Silva
Publicado en enero 2014

Introducción

Cuando desarrollamos aplicaciones web, siempre habrá una momento en que necesitemos cargar archivos a nuestra aplicación. Antes de la versión Java EE 6, la carga de archivos no era natural de la plataforma Java EE. Hacer una rutina de carga de archivos era bastante pesado y complejo, por lo que por lo que se necesitaba de otras bibliotecas Java para cumplir nuestro cometido. En este artículo veremos tres ejemplos de cómo cargar archivos a una aplicación web, sus ventajas y desventajas.

Usando Biblioteca Apache Commons FileUpload

La fundación Apache cuenta con un conjunto de bibliotecas Java llamadas “Apache Commons” (http://commons.apache.org/) que consiste en diversas bibiliotecas que nos ayudarían a varias tareas específicas para nuestras aplicaciones, sea web o desktop. Una de ellas se llama “FileUpload” (http://commons.apache.org/proper/commons-fileupload/)

El formulario donde el usuario colgará el archivo, deberá tener el tipo “multipart/form-data”

<%@page contentType="text/html" pageEncoding="UTF-8"%>
<!DOCTYPE html>
<html>
    <head>
        <meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
        <title>Cargar archivo</title>
    </head>
    <body>
        <h1>Cargar archivo</h1>
        <h2>Usando commons-fileupload</h2>
        <form method="POST" 
              action="${pageContext.servletContext.contextPath}/CargarArchivoServlet" 
              enctype="multipart/form-data">
            <label for="inputFile">Seleccione un archivo</label>
            <input type="file" name="inputFile" id="inputFile" value="" />
            <button>Cargar</button>
        </form>
    </body>
</html>

El HTML es bastante simple, y está claro el contenido: Tiene un input de tipo “file” y el botón. Este form apunta a un servlet que veremos a continuación.

La parte principal del servlet es el siguiente:

    protected void processRequest(HttpServletRequest request, HttpServletResponse response)
            throws ServletException, IOException {
        try {
            FileItemFactory factory = new DiskFileItemFactory();
            ServletContext servletContext = this.getServletConfig().getServletContext();
            File repository = (File) servletContext.getAttribute("javax.servlet.context.tempdir");
            ServletFileUpload upload = new ServletFileUpload(factory);
            List<FileItem> items = upload.parseRequest(request);
            for (FileItem item : items) {
                if (!item.isFormField()) {
                    String fileName = item.getName();
                    String contentType = item.getContentType();
                    long size = item.getSize();
                    request.setAttribute("fileName", fileName);
                    request.setAttribute("contentType", contentType);
                    request.setAttribute("size", size);
                    break;
                }
            }
            RequestDispatcher rd = request.getRequestDispatcher("/resultado.jsp");
            rd.forward(request, response);
        } catch (FileUploadException ex) {
            LOGGER.log(Level.SEVERE, null, ex);
        }
    }

Una vez cargado el contenido, se toman atributos que son guardados en variables de sesión para ser mostrados en otra página.

Lo interesante de este ejemplo es que se necesita preparar la zona donde se guardará el archivo subido: Se crea una fábrica de archivos (FileItemFactory), se prepara el repositorio, y después se continúa evaluando los campos (for) y ver si el campo cargado no es uno tipo formulario (isFormField()) es decir, es un archivo. Una vez identificado, se procede a conocer el contenido.

El archivo resultado.jsp es como sigue:

<%@page contentType="text/html" pageEncoding="UTF-8"%>
<!DOCTYPE html>
<html>
    <head>
        <meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
        <title>Carga de archivo</title>
    </head>
    <body>
        <h1>Resultado de carga</h1>
        <ul>
            <li>fileName:${fileName}</li>
            <li>contentType:${contentType}</li>
            <li>size:${size/1024.0} KB</li>
        </ul>
    </body>
</html>

Ventajas

  • Puede funcionar en cualquier contenedor Java EE que no soporte
  • Mantiene un código limpio en el formulario
  • Puede ser compatible con cualquier framework que estemos usando.

Desventajas

  • Es biblioteca externa que necesita de otra biblioteca. He usado Maven para que descargue las dependencias
  • Al usar bibliotecas externas, y si nuestra aplicación necesitase de otras bibliotecas, debemos tener cuidado en no hacer conflicto de versiones.
  • Se necesita de mucha configuración preliminar a nivel de código antes de manejar los archivos cargados.

Usando Servlet 3.0 - Java EE 6

En Java EE 6 se introduce la capacidad de cargar archivos como parte del API en la versión 3.0 de los Servlets. Con esto, ya no dependeremos de una biblioteca adicional.

Usando el mismo proyecto anterior, podemos cambiar el Servlet con el siguiente contenido.

    protected void processRequest(HttpServletRequest request, HttpServletResponse response)
            throws ServletException, IOException {
        Part filePart = request.getPart("inputFile");
        long size = filePart.getSize();
        String fileName = getFileName(filePart);
        String contentType = filePart.getContentType();

        request.setAttribute("fileName", fileName);
        request.setAttribute("contentType", contentType);
        request.setAttribute("size", size);
        RequestDispatcher rd = request.getRequestDispatcher("/resultado.jsp");
        rd.forward(request, response);
    }

    private String getFileName(final Part part) {
        final String partHeader = part.getHeader("content-disposition");
        LOGGER.log(Level.INFO, "Part Header = {0}", partHeader);
        for (String content : part.getHeader("content-disposition").split(";")) {
            if (content.trim().startsWith("filename")) {
                return content.substring(
                        content.indexOf('=') + 1).trim().replace("\"", "");
            }
        }
        return null;
    }


Ventajas

  • No existe dependencia de bibliotecas externas
  • La manipulación del archivo cargado es mucho más simple que el ejemplo anterior.
  • Mantiene la compatibilidad de uso en el formulario inicial, de tal manera que si se desea migrar de versión de contenedor, no será complicado.
  • Puede ser integrado con otros frameworks de interfaz de usuario.

Desventajas

Usando JSF 2.2 Java EE 7

En Java EE 7, se da más impulso a JavaServer Faces, ya que este framework puede realizar complejas aplicaciones con algunas declaraciones, incluyendo validaciones en los formularios de entrada, ajax, formato de visualización, etc.

El formulario es como sigue:

        <h:form enctype="multipart/form-data">
            <h:panelGrid columns="2">
                <h:outputLabel value="Cargar archivo" for="fileUpload" />
                <h:inputFile value="#{fileUploadFormBean.fileUpload}" id="fileUpload" />

                <h:commandButton value="Cargar"/>
            </h:panelGrid>
        </h:form> 

Cuenta con el tipo “multipart/form-data” como los otros ejemplos. Pero en este caso, el componente “inputFile” - usando el formato de JSF - se asocia con la propiedad del ManagedBean, por lo que no necesita interpretarse el archivo cargado.

Este es el código fuente del ManagedBean:

@Named(value = "fileUploadFormBean")
@RequestScoped
public class FileUploadFormBean {

    private Part fileUpload;

    /**
     * Creates a new instance of FileUploadFormBean
     */
    public FileUploadFormBean() {
    }

    public Part getFileUpload() {
        return fileUpload;
    }

    public void setFileUpload(Part fileUpload) {
        this.fileUpload = fileUpload;
    }
    
}

No se necesita nada de complejidad para usar el archivo cargado. Este es el .xhtml que muestra las propiedades del archivo cargado.

            <h:panelGrid rendered="#{not empty(fileUploadFormBean.fileUpload)}" columns="2"  >
                
                <h:outputText value="fileName:" />
                <h:outputText value="#{fileUploadFormBean.fileUpload.submittedFileName}" />
                
                <h:outputText value="contentType:" />
                <h:outputText value="#{fileUploadFormBean.fileUpload.contentType}" />
                
                <h:outputText value="size:" />
                <h:outputText value="#{fileUploadFormBean.fileUpload.size}" />
            </h:panelGrid>


Ventajas

  • Se reduce bastante código y es más legible al usar ManagedBeans.
  • Puede utilizar Ajax en el mismo formulario.
  • La manipulación del archivo en el lado del servidor es mucho más simple.

Desventajas

  • Solo funciona con JSF 2.2. Nuestra aplicación debe estar basada en JSF 2.2 para poder realizar esta funcionalidad. 

Código fuente

El código fuente para este artículo se encuentran a continuación:

 


Publicado por Diego Silva.