¿Cómo puedo cargar archivos a un servidor usando JSP/Servlet?

Resuelto Thang Pham asked hace 14 años • 14 respuestas

¿Cómo puedo cargar archivos al servidor usando JSP/Servlet?

Probé esto:

<form action="upload" method="post">
    <input type="text" name="description" />
    <input type="file" name="file" />
    <input type="submit" />
</form>

Sin embargo, sólo obtengo el nombre del archivo, no el contenido del archivo. Cuando agrego enctype="multipart/form-data"al <form>, luego request.getParameter()regresanull .

Durante la investigación me topé con Apache Common FileUpload . Probé esto:

FileItemFactory factory = new DiskFileItemFactory();
ServletFileUpload upload = new ServletFileUpload(factory);
List items = upload.parseRequest(request); // This line is where it died.

Desafortunadamente, el servlet arrojó una excepción sin un mensaje ni una causa claros. Aquí está el seguimiento de la pila:

SEVERE: Servlet.service() for servlet UploadServlet threw exception
javax.servlet.ServletException: Servlet execution threw an exception
    at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:313)
    at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:206)
    at org.apache.catalina.core.StandardWrapperValve.invoke(StandardWrapperValve.java:233)
    at org.apache.catalina.core.StandardContextValve.invoke(StandardContextValve.java:191)
    at org.apache.catalina.core.StandardHostValve.invoke(StandardHostValve.java:127)
    at org.apache.catalina.valves.ErrorReportValve.invoke(ErrorReportValve.java:102)
    at org.apache.catalina.core.StandardEngineValve.invoke(StandardEngineValve.java:109)
    at org.apache.catalina.connector.CoyoteAdapter.service(CoyoteAdapter.java:298)
    at org.apache.coyote.http11.Http11Processor.process(Http11Processor.java:852)
    at org.apache.coyote.http11.Http11Protocol$Http11ConnectionHandler.process(Http11Protocol.java:588)
    at org.apache.tomcat.util.net.JIoEndpoint$Worker.run(JIoEndpoint.java:489)
    at java.lang.Thread.run(Thread.java:637)
Thang Pham avatar Mar 11 '10 11:03 Thang Pham
Aceptado

Introducción

Para buscar y seleccionar un archivo para cargar, necesita un <input type="file">campo HTML en el formulario. Como se indica en la especificación HTML, debe utilizar el POSTmétodo y el enctypeatributo del formulario debe establecerse en "multipart/form-data".

<form action="upload" method="post" enctype="multipart/form-data">
    <input type="text" name="description" />
    <input type="file" name="file" />
    <input type="submit" />
</form>

Después de enviar dicho formulario, los datos del formulario binario de varias partes están disponibles en el cuerpo de la solicitud en un formato diferente que cuando enctypeno está configurado.

Antes de Servlet 3.0 (diciembre de 2009), la API de Servlet no admitía de forma nativa multipart/form-data. Solo admite el tipo de formato predeterminado de application/x-www-form-urlencoded. Todos los request.getParameter()consortes y regresarían nullal usar datos de formularios de varias partes. Aquí es donde el conocido Apache Commons FileUpload entró en escena.

¡No lo analices manualmente!

En teoría, puedes analizar el cuerpo de la solicitud tú mismo en función de ServletRequest#getInputStream(). Sin embargo, este es un trabajo preciso y tedioso que requiere un conocimiento preciso de RFC2388 . No deberías intentar hacer esto por tu cuenta o copiar y pegar algún código sin biblioteca local que se encuentre en otras partes de Internet. Muchas fuentes en línea han fracasado rotundamente en esto, como roseindia.net. Véase también carga de archivos pdf . Más bien debería utilizar una biblioteca real que sea utilizada (¡y probada implícitamente!) por millones de usuarios durante años. Una biblioteca de este tipo ha demostrado su solidez.

Cuando ya esté en Servlet 3.0 o posterior, use API nativa

Si está utilizando al menos Servlet 3.0 (Tomcat 7, Jetty 9, JBoss AS 6, GlassFish 3, etc., ya existen desde 2010), entonces puede usar la API estándar proporcionada HttpServletRequest#getPart()para recopilar los elementos de datos individuales del formulario multiparte (la mayoría ¡ Las implementaciones de Servlet 3.0 en realidad usan Apache Commons FileUpload bajo las sábanas para esto!). Además, los campos del formulario normal están disponibles de getParameter()la forma habitual.

Primero anote su servlet para @MultipartConfigpermitirle reconocer y admitir multipart/form-datasolicitudes y así ponerse getPart()a trabajar:

@WebServlet("/upload")
@MultipartConfig
public class UploadServlet extends HttpServlet {
    // ...
}

Luego, implemente su doPost()de la siguiente manera:

protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
    String description = request.getParameter("description"); // Retrieves <input type="text" name="description">
    Part filePart = request.getPart("file"); // Retrieves <input type="file" name="file">
    String fileName = Paths.get(filePart.getSubmittedFileName()).getFileName().toString(); // MSIE fix.
    InputStream fileContent = filePart.getInputStream();
    // ... (do your job here)
}

Nota la Path#getFileName(). Esta es una solución de MSIE para obtener el nombre del archivo. Este navegador envía incorrectamente la ruta completa del archivo junto con el nombre en lugar de solo el nombre del archivo.

En caso de que desee cargar varios archivos a través de cualquiera de los dos multiple="true",

<input type="file" name="files" multiple="true" />

o a la antigua usanza con múltiples entradas,

<input type="file" name="files" />
<input type="file" name="files" />
<input type="file" name="files" />
...

luego puede recopilarlos de la siguiente manera (desafortunadamente no existe un método como request.getParts("files")):

protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
    // ...
    List<Part> fileParts = request.getParts().stream().filter(part -> "files".equals(part.getName()) && part.getSize() > 0).collect(Collectors.toList()); // Retrieves <input type="file" name="files" multiple="true">

    for (Part filePart : fileParts) {
        String fileName = Paths.get(filePart.getSubmittedFileName()).getFileName().toString(); // MSIE fix.
        InputStream fileContent = filePart.getInputStream();
        // ... (do your job here)
    }
}

Cuando aún no esté en Servlet 3.1, obtenga manualmente el nombre del archivo enviado

Tenga en cuenta que Part#getSubmittedFileName()se introdujo en Servlet 3.1 (Tomcat 8, Jetty 9, WildFly 8, GlassFish 4, etc., ya existen desde 2013). Si aún no está en Servlet 3.1 (¿en serio?), entonces necesita un método de utilidad adicional para obtener el nombre del archivo enviado.

private static String getSubmittedFileName(Part part) {
    for (String cd : part.getHeader("content-disposition").split(";")) {
        if (cd.trim().startsWith("filename")) {
            String fileName = cd.substring(cd.indexOf('=') + 1).trim().replace("\"", "");
            return fileName.substring(fileName.lastIndexOf('/') + 1).substring(fileName.lastIndexOf('\\') + 1); // MSIE fix.
        }
    }
    return null;
}
String fileName = getSubmittedFileName(filePart);

Tenga en cuenta la solución de MSIE para obtener el nombre del archivo. Este navegador envía incorrectamente la ruta completa del archivo junto con el nombre en lugar de solo el nombre del archivo.

Cuando aún no esté en Servlet 3.0, use Apache Commons FileUpload

Si aún no tiene Servlet 3.0 (¿no es hora de actualizarlo? ¡Se lanzó hace más de una década!), la práctica común es utilizar Apache Commons FileUpload para analizar las solicitudes de datos de formularios de varias partes. Tiene una excelente Guía del usuario y Preguntas frecuentes (lea ambas con atención). También está el O'Reilly (" cos ") MultipartRequest, pero tiene algunos errores (menores) y ya no se mantiene activamente durante años. No recomendaría usarlo. Apache Commons FileUpload todavía se mantiene activamente y actualmente está muy maduro.

Para utilizar Apache Commons FileUpload, necesita tener al menos los siguientes archivos en su aplicación web /WEB-INF/lib:

  • commons-fileupload.jar
  • commons-io.jar

Su intento inicial falló probablemente porque olvidó el IO común.

Aquí hay un ejemplo inicial de cómo se verá doPost()su archivo cuando use Apache Commons FileUpload:UploadServlet

protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
    try {
        List<FileItem> items = new ServletFileUpload(new DiskFileItemFactory()).parseRequest(request);
        for (FileItem item : items) {
            if (item.isFormField()) {
                // Process regular form field (input type="text|radio|checkbox|etc", select, etc).
                String fieldName = item.getFieldName();
                String fieldValue = item.getString();
                // ... (do your job here)
            } else {
                // Process form file field (input type="file").
                String fieldName = item.getFieldName();
                String fileName = FilenameUtils.getName(item.getName());
                InputStream fileContent = item.getInputStream();
                // ... (do your job here)
            }
        }
    } catch (FileUploadException e) {
        throw new ServletException("Cannot parse multipart request.", e);
    }

    // ...
}

Es muy importante que no llame a getParameter(), getParameterMap(), getParameterValues(), getInputStream(), getReader(), etc con la misma solicitud de antemano. De lo contrario, el contenedor de servlets leerá y analizará el cuerpo de la solicitud y, por lo tanto, Apache Commons FileUpload obtendrá un cuerpo de solicitud vacío. Véase también ServletFileUpload#parseRequest(solicitud) devuelve una lista vacía .

Nota la FilenameUtils#getName(). Esta es una solución de MSIE para obtener el nombre del archivo. Este navegador envía incorrectamente la ruta completa del archivo junto con el nombre en lugar de solo el nombre del archivo.

Alternativamente, también puedes envolver todo esto en un Filterarchivo que lo analice todo automáticamente y vuelva a colocar el material en el mapa de parámetros de la solicitud para que puedas continuar usando request.getParameter()la forma habitual y recuperar el archivo cargado mediante request.getAttribute(). Puede encontrar un ejemplo en este artículo del blog .

Solución alternativa para el error GlassFish3 que getParameter()aún regresanull

Tenga en cuenta que las versiones de Glassfish anteriores a la 3.1.2 tenían un error por el cual el archivo getParameter()todavía regresa null. Si está apuntando a un contenedor de este tipo y no puede actualizarlo, entonces necesita extraer el valor getPart()con la ayuda de este método de utilidad:

private static String getValue(Part part) throws IOException {
    BufferedReader reader = new BufferedReader(new InputStreamReader(part.getInputStream(), "UTF-8"));
    StringBuilder value = new StringBuilder();
    char[] buffer = new char[1024];
    for (int length = 0; (length = reader.read(buffer)) > 0;) {
        value.append(buffer, 0, length);
    }
    return value.toString();
}
String description = getValue(request.getPart("description")); // Retrieves <input type="text" name="description">
    

Guardando el archivo cargado (¡no uses getRealPath()ni part.write()!)

Diríjase a las siguientes respuestas para obtener detalles sobre cómo guardar correctamente lo obtenido InputStream(la fileContentvariable como se muestra en los fragmentos de código anteriores) en el disco o la base de datos:

  • Forma recomendada de guardar archivos cargados en una aplicación de servlet
  • ¿Cómo subir una imagen y guardarla en la base de datos?
  • ¿Cómo convertir Part a Blob para poder almacenarlo en MySQL?

Sirviendo el archivo subido

Diríjase a las siguientes respuestas para obtener detalles sobre cómo entregar correctamente el archivo guardado desde el disco o la base de datos al cliente:

  • Cargue imágenes desde fuera de la carpeta webapps/webcontext/implementar usando la etiqueta <h:graphicImage> o <img>
  • ¿Cómo recuperar y mostrar imágenes de una base de datos en una página JSP?
  • La forma más sencilla de servir datos estáticos desde fuera del servidor de aplicaciones en una aplicación web Java
  • Plantilla abstracta para servlet de recursos estáticos que admite el almacenamiento en caché HTTP

Ajaxificando la forma

Dirígete a las siguientes respuestas sobre cómo cargar usando Ajax (y jQuery). ¡Tenga en cuenta que no es necesario cambiar el código del servlet para recopilar los datos del formulario! Sólo se puede cambiar la forma en que responde, pero esto es bastante trivial (es decir, en lugar de reenviar a JSP, simplemente imprima algo de JSON o XML o incluso texto sin formato dependiendo de lo que espere el script responsable de la llamada Ajax).

  • ¿Cómo puedo cargar archivos a un servidor usando JSP/Servlet y Ajax?
  • Enviar un archivo como multiparte a través de XMLHttpRequest
  • Carga de archivos HTML5 mediante arrastrar y soltar en Java Servlet
BalusC avatar Mar 11 '2010 12:03 BalusC

Si usa Spring MVC , así es como (lo dejo aquí en caso de que alguien lo encuentre útil):

Utilice un formulario con enctypeel atributo establecido en " multipart/form-data" (igual que la respuesta de BalusC ):

<form action="upload" method="post" enctype="multipart/form-data">
    <input type="file" name="file" />
    <input type="submit" value="Upload"/>
</form>

En su controlador, asigne el parámetro de solicitud filepara MultipartFileescribir de la siguiente manera:

@RequestMapping(value = "/upload", method = RequestMethod.POST)
public void handleUpload(@RequestParam("file") MultipartFile file) throws IOException {
    if (!file.isEmpty()) {
            byte[] bytes = file.getBytes(); // alternatively, file.getInputStream();
            // application logic
    }
}

Puede obtener el nombre y el tamaño del archivo usando MultipartFile's getOriginalFilename()y getSize().

Probé esto con la versión Spring 4.1.1.RELEASE.

Amila avatar Aug 26 '2015 12:08 Amila

Sin componentes ni librerías externas en Tomcat 6 o Tomcat 7

Habilitando la carga en el archivo web.xml :

Instalación manual de PHP, Tomcat y Httpd Lounge .

<servlet>
    <servlet-name>jsp</servlet-name>
    <servlet-class>org.apache.jasper.servlet.JspServlet</servlet-class>
    <multipart-config>
      <max-file-size>3145728</max-file-size>
      <max-request-size>5242880</max-request-size>
    </multipart-config>
    <init-param>
        <param-name>fork</param-name>
        <param-value>false</param-value>
    </init-param>
    <init-param>
        <param-name>xpoweredBy</param-name>
        <param-value>false</param-value>
    </init-param>
    <load-on-startup>3</load-on-startup>
</servlet>

Como se puede ver :

<multipart-config>
  <max-file-size>3145728</max-file-size>
  <max-request-size>5242880</max-request-size>
</multipart-config>

Subir archivos usando JSP. archivos:

En el archivo HTML

<form method="post" enctype="multipart/form-data" name="Form" >

  <input type="file" name="fFoto" id="fFoto" value="" /></td>
  <input type="file" name="fResumen" id="fResumen" value=""/>

En el archivo JSP o Servlet

InputStream isFoto = request.getPart("fFoto").getInputStream();
InputStream isResu = request.getPart("fResumen").getInputStream();
ByteArrayOutputStream baos = new ByteArrayOutputStream();
byte buf[] = new byte[8192];
int qt = 0;
while ((qt = isResu.read(buf)) != -1) {
  baos.write(buf, 0, qt);
}
String sResumen = baos.toString();

Edite su código según los requisitos del servlet, como max-file-size , max-request-size y otras opciones que puede configurar...

joseluisbz avatar Jan 25 '2014 05:01 joseluisbz

Necesita que el common-io.1.4.jararchivo esté incluido en su libdirectorio, o si está trabajando en cualquier editor, como NetBeans, entonces debe ir a las propiedades del proyecto y simplemente agregar el archivo JAR y listo.

Para obtener el common.io.jararchivo, simplemente búsquelo en Google o simplemente vaya al sitio web de Apache Tomcat , donde tendrá la opción de descargarlo de forma gratuita. Pero recuerda una cosa: descarga el archivo ZIP binario si eres usuario de Windows.

Pranav avatar May 17 '2012 11:05 Pranav

Estoy usando un Servlet común para cada formulario HTML, tenga o no archivos adjuntos.

Este servlet devuelve un TreeMapdonde las claves son parámetros de nombre JSP y los valores son entradas del usuario y guarda todos los archivos adjuntos en un directorio fijo y luego cambia el nombre del directorio de su elección. Aquí Connections es nuestra interfaz personalizada que tiene un objeto de conexión.

public class ServletCommonfunctions extends HttpServlet implements
        Connections {

    private static final long serialVersionUID = 1L;

    public ServletCommonfunctions() {}

    protected void doPost(HttpServletRequest request,
                          HttpServletResponse response) throws ServletException,
                          IOException {}

    public SortedMap<String, String> savefilesindirectory(
            HttpServletRequest request, HttpServletResponse response)
            throws IOException {

        // Map<String, String> key_values = Collections.synchronizedMap(new
        // TreeMap<String, String>());
        SortedMap<String, String> key_values = new TreeMap<String, String>();
        String dist = null, fact = null;
        PrintWriter out = response.getWriter();
        File file;
        String filePath = "E:\\FSPATH1\\2KL06CS048\\";
        System.out.println("Directory Created   ????????????"
            + new File(filePath).mkdir());
        int maxFileSize = 5000 * 1024;
        int maxMemSize = 5000 * 1024;

        // Verify the content type
        String contentType = request.getContentType();
        if ((contentType.indexOf("multipart/form-data") >= 0)) {
            DiskFileItemFactory factory = new DiskFileItemFactory();
            // Maximum size that will be stored in memory
            factory.setSizeThreshold(maxMemSize);
            // Location to save data that is larger than maxMemSize.
            factory.setRepository(new File(filePath));
            // Create a new file upload handler
            ServletFileUpload upload = new ServletFileUpload(factory);
            // maximum file size to be uploaded.
            upload.setSizeMax(maxFileSize);
            try {
                // Parse the request to get file items.
                @SuppressWarnings("unchecked")
                List<FileItem> fileItems = upload.parseRequest(request);
                // Process the uploaded file items
                Iterator<FileItem> i = fileItems.iterator();
                while (i.hasNext()) {
                    FileItem fi = (FileItem) i.next();
                    if (!fi.isFormField()) {
                        // Get the uploaded file parameters
                        String fileName = fi.getName();
                        // Write the file
                        if (fileName.lastIndexOf("\\") >= 0) {
                            file = new File(filePath
                                + fileName.substring(fileName
                                        .lastIndexOf("\\")));
                        } else {
                            file = new File(filePath
                                + fileName.substring(fileName
                                        .lastIndexOf("\\") + 1));
                        }
                        fi.write(file);
                    } else {
                        key_values.put(fi.getFieldName(), fi.getString());
                    }
                }
            } catch (Exception ex) {
                System.out.println(ex);
            }
        }
        return key_values;
    }
}
Nagappa L M avatar Jan 08 '2013 05:01 Nagappa L M