如何使用JSP/Servlet上传文件到服务器?

我试了一下:

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

但是,我只得到了文件名,而不是文件内容。当我添加enctype="multipart/form-data"到<form>时,request.getParameter()返回null。

在研究过程中,我偶然发现了Apache Common FileUpload。我试了一下:

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

不幸的是,servlet抛出了一个异常,没有明确的消息和原因。下面是stacktrace:

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)

当前回答

在Tomcat 6或Tomcat 7中没有组件或外部库

在web.xml文件中启用上传:

手动安装PHP、Tomcat和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>

如你所见:

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

使用JSP上传文件。文件:

在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=""/>

在JSP文件或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();

编辑您的代码以满足servlet的要求,如max-file-size, max-request-size和其他选项,您可以设置…

其他回答

如果你碰巧使用Spring MVC,这是如何(我把它留在这里,以防有人发现它有用):

使用一个enctype属性设置为"multipart/form-data"的表单(与BalusC的答案相同):

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

在控制器中,将请求参数文件映射为MultipartFile类型,如下所示:

@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
    }
}

你可以使用MultipartFile的getOriginalFilename()和getSize()来获取文件名和大小。

我已经用Spring版本4.1.1.RELEASE测试了这个功能。

为文件发送多个文件,我们必须使用enctype="multipart/form-data"。

如果要发送多个文件,在input标签中使用multiple="multiple":

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

对于Spring MVC

我设法有一个更简单的版本,用于获取表单输入,包括数据和图像。

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

要处理的控制器

@Controller
public class FormController {
    @RequestMapping(value="/handleform",method= RequestMethod.POST)
    ModelAndView register(@RequestParam String name, @RequestParam int age, @RequestParam MultipartFile file)
            throws ServletException, IOException {

        System.out.println(name);
        System.out.println(age);
        if(!file.isEmpty()){
            byte[] bytes = file.getBytes();
            String filename = file.getOriginalFilename();
            BufferedOutputStream stream =new BufferedOutputStream(new FileOutputStream(new File("D:/" + filename)));
            stream.write(bytes);
            stream.flush();
            stream.close();
        }
        return new ModelAndView("index");
    }
}

HTML页面

<html>
    <head>
        <title>File Uploading Form</title>
    </head>

    <body>
        <h3>File Upload:</h3>
        Select a file to upload: <br />
        <form action="UploadServlet" method="post"
              enctype="multipart/form-data">

            <input type="file" name="file" size="50" />
            <br />
            <input type="submit" value="Upload File" />
        </form>
    </body>
</html>

Servlet文件

// Import required java libraries
import java.io.*;
import java.util.*;

import javax.servlet.ServletConfig;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import org.apache.commons.fileupload.FileItem;
import org.apache.commons.fileupload.FileUploadException;
import org.apache.commons.fileupload.disk.DiskFileItemFactory;
import org.apache.commons.fileupload.servlet.ServletFileUpload;
import org.apache.commons.io.output.*;

public class UploadServlet extends HttpServlet {

    private boolean isMultipart;
    private String filePath;
    private int maxFileSize = 50 * 1024;
    private int maxMemSize = 4 * 1024;
    private File file;

    public void init() {
        // Get the file location where it would be stored.
        filePath =
               getServletContext().getInitParameter("file-upload");
    }

    public void doPost(HttpServletRequest request,
                       HttpServletResponse response)
               throws ServletException, java.io.IOException {

        // Check that we have a file upload request
        isMultipart = ServletFileUpload.isMultipartContent(request);
        response.setContentType("text/html");
        java.io.PrintWriter out = response.getWriter();
        if (!isMultipart) {
            out.println("<html>");
            out.println("<head>");
            out.println("<title>Servlet upload</title>");
            out.println("</head>");
            out.println("<body>");
            out.println("<p>No file uploaded</p>");
            out.println("</body>");
            out.println("</html>");
            return;
        }

        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("c:\\temp"));

        // 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.
            List fileItems = upload.parseRequest(request);

            // Process the uploaded file items
            Iterator i = fileItems.iterator();

            out.println("<html>");
            out.println("<head>");
            out.println("<title>Servlet upload</title>");
            out.println("</head>");
            out.println("<body>");
            while (i.hasNext())
            {
                FileItem fi = (FileItem)i.next();
                if (!fi.isFormField())
                {
                     // Get the uploaded file parameters
                     String fieldName = fi.getFieldName();
                     String fileName = fi.getName();
                     String contentType = fi.getContentType();
                     boolean isInMemory = fi.isInMemory();
                     long sizeInBytes = fi.getSize();

                     // 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);
                     out.println("Uploaded Filename: " + fileName + "<br>");
                }
            }
            out.println("</body>");
            out.println("</html>");
        }
        catch(Exception ex) {
            System.out.println(ex);
        }
    }

    public void doGet(HttpServletRequest request,
                        HttpServletResponse response)
            throws ServletException, java.io.IOException {

        throw new ServletException("GET method used with " +
                 getClass().getName() + ": POST method required.");
    }
}

文件web . xml

编译上面的servlet UploadServlet并在web.xml文件中创建所需的条目,如下所示。

<servlet>
   <servlet-name>UploadServlet</servlet-name>
   <servlet-class>UploadServlet</servlet-class>
</servlet>

<servlet-mapping>
   <servlet-name>UploadServlet</servlet-name>
   <url-pattern>/UploadServlet</url-pattern>
</servlet-mapping>

介绍

要浏览并选择要上传的文件,您需要表单中的HTML <input type="file">字段。在HTML规范中,你必须使用POST方法,并且表单的enctype属性必须设置为“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>

在提交这样的表单之后,请求体中的二进制多部分表单数据与未设置enctype时的格式不同。

在Servlet 3.0(2009年12月)之前,Servlet API不支持多部分/表单数据。它只支持application/x-www-form-urlencoded的默认表单封装类型。当使用多部分表单数据时,request.getParameter()和consorts都将返回null。这就是著名的Apache Commons FileUpload出现的地方。

不要手动解析!

You can in theory parse the request body yourself based on ServletRequest#getInputStream(). However, this is a precise and tedious work which requires precise knowledge of RFC2388. You shouldn't try to do this on your own or copypaste some homegrown library-less code found elsewhere on the Internet. Many online sources have failed hard in this, such as roseindia.net. See also uploading of pdf file. You should rather use a real library which is used (and implicitly tested!) by millions of users for years. Such a library has proven its robustness.

当您已经使用Servlet 3.0或更新版本时,请使用本机API

如果你至少在使用Servlet 3.0 (Tomcat 7, Jetty 9, JBoss AS 6, GlassFish 3,等等,他们早在2010年就已经存在了),那么你可以使用标准API提供的HttpServletRequest#getPart()来收集单独的多部分表单数据项(大多数Servlet 3.0实现实际上使用Apache Commons FileUpload来实现这一点!)此外,普通表单字段可以通过getParameter()以通常的方式获得。

首先用@MultipartConfig注释你的servlet,让它识别和支持多部分/表单数据请求,从而让getPart()工作:

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

然后,实现它的doPost(),如下所示:

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

注意路径#getFileName()。这是MSIE对获取文件名的修复。此浏览器错误地沿文件名发送完整的文件路径,而不是仅发送文件名。

如果你想通过multiple="true"上传多个文件,

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

或者是传统的多输入方式,

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

然后你可以按照下面的方法收集它们(不幸的是没有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)
    }
}

当您还没有使用Servlet 3.1时,手动获取提交的文件名

请注意,#getSubmittedFileName()部分是在Servlet 3.1中引入的(Tomcat 8, Jetty 9, WildFly 8, GlassFish 4等,它们从2013年就已经存在了)。如果您还没有使用Servlet 3.1(真的吗?),那么您需要一个额外的实用工具方法来获取提交的文件名。

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

请注意MSIE在获取文件名方面的修复。此浏览器错误地沿文件名发送完整的文件路径,而不是仅发送文件名。

如果您还没有使用Servlet 3.0,请使用Apache Commons FileUpload

If you're not on Servlet 3.0 yet (isn't it about time to upgrade? it's released over a decade ago!), the common practice is to make use of Apache Commons FileUpload to parse the multpart form data requests. It has an excellent User Guide and FAQ (carefully go through both). There's also the O'Reilly ("cos") MultipartRequest, but it has some (minor) bugs and isn't actively maintained anymore for years. I wouldn't recommend using it. Apache Commons FileUpload is still actively maintained and currently very mature.

为了使用Apache Commons FileUpload,你需要在你的webapp的/WEB-INF/lib中至少有以下文件:

commons-fileupload.jar commons-io.jar

您最初的尝试失败很可能是因为您忘记了公共IO。

下面是一个启动示例,当使用Apache Commons FileUpload时,你的UploadServlet的doPost()可能看起来像:

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

    // ...
}

非常重要的一点是,不要预先在同一个请求上调用getParameter()、getParameterMap()、getParameterValues()、getInputStream()、getReader()等。否则servlet容器将读取并解析请求体,因此Apache Commons FileUpload将得到一个空请求体。请参见a.o. ServletFileUpload#parseRequest(request)返回一个空列表。

注意FilenameUtils#getName()。这是MSIE对获取文件名的修复。此浏览器错误地沿文件名发送完整的文件路径,而不是仅发送文件名。

或者,你也可以把这些都包装在一个Filter中,它会自动解析,并把这些东西放回请求的参数映射中,这样你就可以继续以通常的方式使用request. getparameter(),并通过request. getattribute()检索上传的文件。你可以在这篇博客文章中找到一个例子。

解决GlassFish3 getParameter()仍然返回null的bug

注意,Glassfish 3.1.2以上的版本有一个bug,其中getParameter()仍然返回null。如果你的目标是这样一个容器,并且不能升级它,那么你需要在这个实用工具方法的帮助下从getPart()中提取值:

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">
    

保存上传的文件(不要使用getRealPath()或part.write()!)

关于正确保存获得的InputStream(上面代码片段中显示的fileContent变量)到磁盘或数据库的详细信息,请参阅以下答案:

在servlet应用程序中保存上传文件的推荐方法 如何上传图片并保存在数据库中? 如何将部分转换为Blob,这样我就可以将它存储在MySQL?

服务上传文件

有关如何将保存的文件从磁盘或数据库返回到客户端的详细信息,请参阅以下回答:

使用<h:graphicImage>或<img>标签从webapps / webcontext / deploy文件夹外部加载图像 如何在JSP页面中检索和显示数据库中的图像? 在Java web应用程序中提供来自应用程序服务器外部的静态数据的最简单方法 支持HTTP缓存的静态资源servlet抽象模板

ajax化的形式

下面是如何使用Ajax(和jQuery)上传的答案。请注意,不需要为此更改用于收集表单数据的servlet代码!只有响应的方式可能会改变,但这相当简单(例如,不转发到JSP,只打印一些JSON或XML,甚至纯文本,这取决于负责Ajax调用的脚本所期望的内容)。

如何使用JSP/Servlet和Ajax将文件上传到服务器? 通过XMLHttpRequest以多部分形式发送文件 HTML5拖放文件上传到Java Servlet


希望这一切都有帮助:)