【问题标题】:How can I upload files to a server using JSP/Servlet?如何使用 JSP/Servlet 将文件上传到服务器?
【发布时间】:2016-06-18 10:17:46
【问题描述】:

如何使用 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" 添加到&lt;form&gt; 时,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 在没有明确消息和原因的情况下引发了异常。这是堆栈跟踪:

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)

【问题讨论】:

  • 或许这篇文章会有所帮助:baeldung.com/upload-file-servlet
  • @Adam:他们抄袭了我的回答,并在上面添加了一个广告侦探,试图用它来赚钱。是的,很棒的文章..
  • 不,实际上没有复制任何内容。我写了那篇文章的初稿以及补充代码。核心参考文档可以在这里找到:commons.apache.org/proper/commons-fileupload/using.html(链接到文章并在文章中引用)。示例部分来自核心参考文档(这是参考文档的点 - 即作为参考点),但不是全部(请注意参考文档没有详细说明)。谢谢!

标签: java jsp jakarta-ee servlets file-upload


【解决方案1】:

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

web.xml 文件中启用上传:

Manually Installing PHP, Tomcat and 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-sizemax-request-size 以及您可以设置的其他选项...

【讨论】:

    【解决方案2】:

    您首先必须将表单的 enctype 属性设置为“multipart/form-data”

    如下所示。

    <form action="Controller" method="post" enctype="multipart/form-data">
         <label class="file-upload"> Click here to upload an Image </label>
         <input type="file" name="file" id="file" required>
    </form>
    

    然后,在 Servlet“控制器”中添加 Annotation for a Multi-part 以指示在 servlet 中处理多部分数据。

    完成此操作后,检索通过表单发送的部分,然后检索提交文件的文件名(带路径)。使用它在所需路径中创建一个新文件,并将文件的部分写入新创建的文件以重新创建文件。

    如下图:

    @MultipartConfig
    
    public class Controller extends HttpServlet {
    
        protected void doPost(HttpServletRequest request, HttpServletResponse response)
                throws ServletException, IOException {
            insertImage(request, response);
        }
    
        private void addProduct(HttpServletRequest request, HttpServletResponse response) {
            Part filePart = request.getPart("file");
            String imageName = Paths.get(filePart.getSubmittedFileName()).getFileName().toString();
    
            String imageSavePath = "specify image path to save image"; //path to save image
            FileOutputStream outputStream = null;
            InputStream fileContent = null;
    
            try {
                outputStream = new FileOutputStream(new File(imageSavePath + File.separator + imageName));
                // Creating a new file with file path and the file name
                fileContent = filePart.getInputStream();
                // Getting the input stream
                int readBytes = 0;
                byte[] readArray = new byte[1024];
                // Initializing a byte array with size 1024
    
                while ((readBytes = fileContent.read(readArray)) != -1) {
                    outputStream.write(readArray, 0, readBytes);
                } // This loop will write the contents of the byte array unitl the end to the output stream
            } catch (Exception ex) {
                System.out.println("Error Writing File: " + ex);
            } finally {
                if (outputStream != null) {
                    outputStream.close();
                    // Closing the output stream
                }
                if (fileContent != null) {
                    fileContent.close();
                    // Closing the input stream
                }
            }
        }
    }
    

    【讨论】:

    • 这个解决方案是不同的。其他解决方案使用一个库来处理文件,因为这没有第三方 jar 文件。
    • 当前接受的答案已经涵盖了这一点。你读过吗?自 2009 年 12 月以来,本机 API 已经存在。顺便说一下,您关闭流的方式也是遗留的。从 2011 年 7 月推出的 Java 7 开始,您可以使用 try-with-resources 语句而不是在 finally 中摆弄 nullchecks。
    【解决方案3】:

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

    【讨论】:

    • 你能分享选择图像表格db mysql并在jsp/html上显示吗?
    【解决方案4】:

    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>
    

    【讨论】:

      【解决方案5】:

      我为每个 HTML 表单使用一个通用的Servlet,无论它是否有附件。

      此 Servlet 返回一个 TreeMap,其中键是 JSP 名称参数,值是用户输入,并将所有附件保存在固定目录中,稍后您可以重命名您选择的目录。这里的 Connections 是我们的自定义接口,有一个连接对象。

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

      【讨论】:

      • @buhake sindi 嘿,如果我使用实时服务器或者我通过将文件上传到服务器来实现我的项目,那么文件路径应该是什么
      • 实时服务器中的任何目录。如果您编写代码在servlet中创建目录,那么目录将在实时服务器中创建
      【解决方案6】:

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

      使用enctype 属性设置为“multipart/form-data”的表单(与BalusC's answer 相同):

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

      在您的控制器中,将请求参数file 映射到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
          }
      }
      

      您可以使用MultipartFilegetOriginalFilename()getSize() 获取文件名和大小。

      我已经用 Spring 版本 4.1.1.RELEASE 对此进行了测试。

      【讨论】:

      • 如果我没记错的话,这需要您在服务器的应用程序配置中配置一个 bean...
      【解决方案7】:

      您可以使用 JSP /servlet 上传文件。

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

      另一方面,在服务器端,使用以下代码。

      package com.abc..servlet;
      
      import java.io.File;
      ---------
      --------
      
      
      /**
       * Servlet implementation class UploadFileServlet
       */
      public class UploadFileServlet extends HttpServlet {
          private static final long serialVersionUID = 1L;
      
          public UploadFileServlet() {
              super();
              // TODO Auto-generated constructor stub
          }
          protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
              // TODO Auto-generated method stub
              response.sendRedirect("../jsp/ErrorPage.jsp");
          }
      
          protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
              // TODO Auto-generated method stub
      
              PrintWriter out = response.getWriter();
              HttpSession httpSession = request.getSession();
              String filePathUpload = (String) httpSession.getAttribute("path") != null ? httpSession.getAttribute("path").toString() : "" ;
      
              String path1 = filePathUpload;
              String filename = null;
              File path = null;
              FileItem item = null;
      
      
              boolean isMultipart = ServletFileUpload.isMultipartContent(request);
      
              if (isMultipart) {
                  FileItemFactory factory = new DiskFileItemFactory();
                  ServletFileUpload upload = new ServletFileUpload(factory);
                  String FieldName = "";
                  try {
                      List items = upload.parseRequest(request);
                      Iterator iterator = items.iterator();
                      while (iterator.hasNext()) {
                           item = (FileItem) iterator.next();
      
                              if (fieldname.equals("description")) {
                                  description = item.getString();
                              }
                          }
                          if (!item.isFormField()) {
                              filename = item.getName();
                              path = new File(path1 + File.separator);
                              if (!path.exists()) {
                                  boolean status = path.mkdirs();
                              }
                              /* Start of code fro privilege */
      
                              File uploadedFile = new File(path + Filename);  // for copy file
                              item.write(uploadedFile);
                              }
                          } else {
                              f1 = item.getName();
                          }
      
                      } // END OF WHILE
                      response.sendRedirect("welcome.jsp");
                  } catch (FileUploadException e) {
                      e.printStackTrace();
                  } catch (Exception e) {
                      e.printStackTrace();
                  }
              }
          }
      }
      

      【讨论】:

      • 你说的"Start of code fro privilege"是什么意思(好像不太明白)?请通过editing (changing) your answer 回复,而不是在 cmets 中(without "Edit:"、"Update:" 或类似的 - 答案应该看起来像是今天写的)。
      【解决方案8】:

      用途:

      DiskFileUpload upload = new DiskFileUpload();
      

      你必须从这个对象中获取文件项和字段,然后你可以像下面这样存储到服务器中:

      String loc = "./webapps/prjct name/server folder/" + contentid + extension;
      File uploadFile = new File(loc);
      item.write(uploadFile);
      

      【讨论】:

        【解决方案9】:

        如果您将Geronimo 与它的嵌入式Tomcat 一起使用,则会出现此问题的另一个来源。在这种情况下,在多次测试Commons IO 和commons-fileupload 之后,问题出在处理commons-xxx JAR 文件的父类加载器上。必须防止这种情况。崩溃总是发生在:

        fileItems = uploader.parseRequest(request);
        

        请注意,当前版本的 commons-fileupload 已将 fileItems 的 List 类型更改为特定的 List&lt;FileItem&gt;,而不是之前的通用版本 List

        我将 commons-fileupload 和 Commons IO 的源代码添加到我的 Eclipse 项目中以跟踪实际错误并最终获得一些见解。首先,抛出的异常是 Throwable 类型,不是所述的 FileIOException 甚至是 Exception(这些不会被捕获)。其次,错误消息很容易混淆,因为它声明类未找到,因为axis2 找不到commons-io。我的项目中根本没有使用 Axis2,但它作为标准安装的一部分存在于 Geronimo 存储库子目录中的文件夹中。

        最后,我找到了一个提出有效解决方案的地方,成功解决了我的问题。您必须在部署计划中对父加载器隐藏 JAR 文件。这被放入 geronimo-web.xml 文件中,我的完整文件如下所示。

        粘贴自http://osdir.com/ml/user-geronimo-apache/2011-03/msg00026.html

        <?xml version="1.0" encoding="UTF-8" standalone="no"?>
        <web:web-app xmlns:app="http://geronimo.apache.org/xml/ns/j2ee/application-2.0" xmlns:client="http://geronimo.apache.org/xml/ns/j2ee/application-client-2.0" xmlns:conn="http://geronimo.apache.org/xml/ns/j2ee/connector-1.2" xmlns:dep="http://geronimo.apache.org/xml/ns/deployment-1.2" xmlns:ejb="http://openejb.apache.org/xml/ns/openejb-jar-2.2" xmlns:log="http://geronimo.apache.org/xml/ns/loginconfig-2.0" xmlns:name="http://geronimo.apache.org/xml/ns/naming-1.2" xmlns:pers="http://java.sun.com/xml/ns/persistence" xmlns:pkgen="http://openejb.apache.org/xml/ns/pkgen-2.1" xmlns:sec="http://geronimo.apache.org/xml/ns/security-2.0" xmlns:web="http://geronimo.apache.org/xml/ns/j2ee/web-2.0.1">
            <dep:environment>
                <dep:moduleId>
                    <dep:groupId>DataStar</dep:groupId>
                    <dep:artifactId>DataStar</dep:artifactId>
                    <dep:version>1.0</dep:version>
                    <dep:type>car</dep:type>
                </dep:moduleId>
        
                <!-- Don't load commons-io or fileupload from parent classloaders -->
                <dep:hidden-classes>
                    <dep:filter>org.apache.commons.io</dep:filter>
                    <dep:filter>org.apache.commons.fileupload</dep:filter>
                </dep:hidden-classes>
                <dep:inverse-classloading/>
        
            </dep:environment>
            <web:context-root>/DataStar</web:context-root>
        </web:web-app>
        

        【讨论】:

        • 链接(实际上)已损坏(重定向到https://osdir.com/) - HTTPS 版本也是如此。
        【解决方案10】:

        为文件发送多个文件,我们必须使用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>
        

        【讨论】:

        • 我们将如何做 getPart("fileattachments") 以便我们得到一个 Parts 数组?我认为多个文件的 getPart 不会起作用?
        • “为文件发送多个文件”是什么意思(似乎难以理解)?请通过editing (changing) your answer 回复,而不是在 cmets 中(without "Edit:"、"Update:" 或类似的 - 问题/答案应该看起来像今天写的)。
        【解决方案11】:

        我可以为文件和输入控件想出的最简单方法,无需十亿个库:

          <%
              if (request.getContentType() == null)
                  return;
              // For input type=text controls
              String v_Text =
                  (new BufferedReader(new InputStreamReader(request.getPart("Text1").getInputStream()))).readLine();
        
              // For input type=file controls
              InputStream inStr = request.getPart("File1").getInputStream();
              char charArray[] = new char[inStr.available()];
              new InputStreamReader(inStr).read(charArray);
              String contents = new String(charArray);
          %>
        

        【讨论】:

        • &lt;% 是干什么用的? ASP.NET (C#)?你能澄清一下吗?请通过editing (changing) your answer 回复,而不是在 cmets 中(without "Edit:"、"Update:" 或类似的 - 答案应该看起来像是今天写的)。
        【解决方案12】:

        简介

        要浏览并选择要上传的文件,您需要表单中的 HTML &lt;input type="file"&gt; 字段。如HTML specification 中所述,您必须使用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 时相比,a different format 的请求正文中提供二进制多部分表单数据。

        在 Servlet 3.0 之前,Servlet API 本身并不支持multipart/form-data。它仅支持application/x-www-form-urlencoded 的默认形式enctype。当使用多部分表单数据时,request.getParameter() 和 consorts 都将返回 null。这就是众所周知的Apache Commons FileUpload 出现的地方。

        不要手动解析!

        理论上,您可以根据ServletRequest#getInputStream() 自己解析请求正文。然而,这是一项精确而乏味的工作,需要精确了解RFC2388。您不应该尝试自己执行此操作或复制粘贴在 Internet 上其他地方找到的一些本地开发的无库代码。许多在线资源在这方面都失败了,例如roseindia.net。另见uploading of pdf file。您应该使用数百万用户多年来使用(并经过隐式测试!)的真实库。这样的库已经证明了它的稳健性。

        如果您已经使用 Servlet 3.0 或更高版本,请使用本机 API

        如果您至少使用 Servlet 3.0(Tomcat 7、Jetty 9、JBoss AS 6、GlassFish 3 等),那么您可以使用HttpServletRequest#getPart() 提供的标准 API 来收集单独的多部分表单数据项(大多数Servlet 3.0 实现实际上为此使用了 Apache Commons FileUpload!)。此外,getParameter() 可以通过常规方式使用普通表单字段。

        首先使用@MultipartConfig 注释您的servlet,以便让它识别和支持multipart/form-data 请求,从而让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)
        }
        

        注意Path#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 时,手动获取提交的文件名

        请注意,Part#getSubmittedFileName() 是在 Servlet 3.1(Tomcat 8、Jetty 9、WildFly 8、GlassFish 4 等)中引入的。如果您还没有使用 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

        如果您还没有使用 Servlet 3.0(是不是该升级了?),通常的做法是使用Apache Commons FileUpload 来解析多部分表单数据请求。它有一个出色的User GuideFAQ(仔细检查两者)。还有 O'Reilly ("cos") MultipartRequest,但它有一些(小)错误,并且多年来不再积极维护。我不建议使用它。 Apache Commons FileUpload 仍在积极维护中,目前非常成熟。

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

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

        以下是使用 Apache Commons FileUpload 时 UploadServletdoPost() 的启动示例:

        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) returns an empty list.

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

        或者,您也可以将所有内容包装在 Filter 中,它会自动解析所有内容并将内容放回请求的参数映射中,以便您可以继续使用 request.getParameter() 并通过 @ 检索上传的文件987654393@。 You can find an example in this blog article.

        getParameter() 的 GlassFish3 错误的解决方法仍然返回 null

        请注意,早于 3.1.2 的 Glassfish 版本具有 a 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(上​​述代码sn-ps中显示的fileContent变量)保存到磁盘或数据库的详细信息,请参阅以下答案:

        提供上传的文件

        请参阅以下答案,详细了解如何将已保存的文件从磁盘或数据库中正确返回给客户端:

        Ajaxifying 表单

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


        希望这一切都有帮助:)

        【讨论】:

        • 啊抱歉,我看到 request.getParts("file") 并且很困惑 x_x
        • 对于 Servlet 3.0,如果违反了 MultipartConfig 条件(例如:maxFileSize),则调用 request.getParameter() 返回 null。这是故意的吗?如果我在调用getPart(并检查IllegalStateException)之前得到一些常规(文本)参数怎么办?这会导致在我有机会检查 IllegalStateException 之前抛出 NullPointerException
        • @BalusC 我创建了一个与此相关的帖子,你知道我如何从 File API webKitDirectory 中检索额外的信息。更多细节在这里stackoverflow.com/questions/45419598/…
        • 是的,如果有人尝试将 3.0 部分中的代码与 tomcat 7 一起使用,他们可能会在String fileName = Paths.get(filePart.getSubmittedFileName()).getFileName().toString(); // MSIE fix.part 中遇到与我类似的问题
        • @aaa:当您使用 Reader 和/或 Writer 将字节转换为字符时,可能会发生这种情况,原因不明。不要那样做。在读取和写入上传的文件期间,在所有地方使用InputStream/OutputStream,而不将字节转换为字符。 PDF 文件不是基于字符的文本文件。它是一个二进制文件。
        【解决方案13】:

        这是一个使用 apache commons-fileupload 的示例:

        // apache commons-fileupload to handle file upload
        DiskFileItemFactory factory = new DiskFileItemFactory();
        factory.setRepository(new File(DataSources.TORRENTS_DIR()));
        ServletFileUpload fileUpload = new ServletFileUpload(factory);
        
        List<FileItem> items = fileUpload.parseRequest(req.raw());
        FileItem item = items.stream()
          .filter(e ->
          "the_upload_name".equals(e.getFieldName()))
          .findFirst().get();
        String fileName = item.getName();
        
        item.write(new File(dir, fileName));
        log.info(fileName);
        

        【讨论】:

          【解决方案14】:

          您需要将common-io.1.4.jar 文件包含在您的lib 目录中,或者如果您正在使用任何编辑器(如NetBeans),那么您需要转到项目属性并添加JAR 文件,您将完成。

          要获取common.io.jar 文件,只需在谷歌上搜索或访问 Apache Tomcat 网站,您可以选择免费下载此文件。但请记住一件事:如果您是 Windows 用户,请下载二进制 ZIP 文件。

          【讨论】:

          • 找不到.jar.zip。你的意思是.zip
          猜你喜欢
          相关资源
          最近更新 更多