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

Tha*_*ham 671 java jsp servlets file-upload java-ee

如何使用JSP/Servlet将文件上传到服务器?我试过这个:

<form action="upload" method="post">
    <input type="text" name="description" />
    <input type="file" name="file" />
    <input type="submit" />
</form>
Run Code Online (Sandbox Code Playgroud)

但是,我只获取文件名,而不是文件内容.当我添加 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.
Run Code Online (Sandbox Code Playgroud)

不幸的是,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)
Run Code Online (Sandbox Code Playgroud)

Bal*_*usC 1161

介绍

要浏览并选择要上载的文件,您需要<input type="file">在表单中使用HTML 字段.如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>
Run Code Online (Sandbox Code Playgroud)

提交此类表单后,请求正文中的二进制多部分表单数据的格式enctype未设置时的格式不同.

在Servlet 3.0之前,Servlet API本身不支持multipart/form-data.它仅支持默认的表单enctype application/x-www-form-urlencoded.在request.getParameter()和配偶都会返回null使用多形式的数据时.这是众所周知的Apache Commons FileUpload进入图片的地方.

不要手动解析它!

理论上,您可以基于自己解析请求体ServletRequest#getInputStream().但是,这是一项精确而繁琐的工作,需要精确了解RFC2388.你不应该试图自己动手做,或者在互联网上的其他地方找到一些本土的无库代码.许多在线消息来源都很难,例如roseindia.net.另见pdf文件的上传.您应该使用数百万用户多年使用(并隐式测试!)的真实库.这样的库已经证明了它的稳健性.

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

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

首先注释你的servlet,@MultipartConfig以便让它识别和支持multipart/form-data请求,从而开始getPart()工作:

@WebServlet("/upload")
@MultipartConfig
public class UploadServlet extends HttpServlet {
    // ...
}
Run Code Online (Sandbox Code Playgroud)

然后,实现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)
}
Run Code Online (Sandbox Code Playgroud)

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

如果您<input type="file" name="file" multiple="true" />有多文件上传,请按以下方式收集它们(遗憾的是没有这样的方法request.getParts("file")):

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

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

如果您还没有使用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;
}
Run Code Online (Sandbox Code Playgroud)
String fileName = getSubmittedFileName(filePart);
Run Code Online (Sandbox Code Playgroud)

请注意MSIE修复以获取文件名.此浏览器错误地沿名称发送完整文件路径,而不是仅发送文件名.

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

如果您还没有使用Servlet 3.0(现在不是升级时间吗?),通常的做法是使用Apache Commons FileUpload来解析多部分表单数据请求.它有一个优秀的用户指南常见问题解答(仔细阅读两者).还有O'Reilly(" cos ")MultipartRequest,但它有一些(小的)错误,并且多年来不再积极维护.我不建议使用它.Apache Commons FileUpload仍然在积极维护,目前非常成熟.

要使用Apache Commons FileUpload,您需要在webapp中至少包含以下文件/WEB-INF/lib:

您最初的尝试失败的可能性很大,因为您忘记了公共IO.

以下是使用Apache Commons FileUpload时doPost()您的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);
    }

    // ...
}
Run Code Online (Sandbox Code Playgroud)

这是非常重要的,你不叫getParameter(),getParameterMap(),getParameterValues(),getInputStream(),getReader(),等上了同样的要求提前.否则,servlet容器将读取并解析请求体,因此Apache Commons FileUpload将获得一个空的请求体.另请参见ao ServletFileUpload #parseRequest(request)返回一个空列表.

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

或者,您也可以将其全部包装在一个Filter自动解析所有内容中并将这些内容放回请求的参数图中,以便您可以继续使用request.getParameter()常规方法并检索上传的文件request.getAttribute().您可以在此博客文章中找到一个示例.

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

需要注意的是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();
}
Run Code Online (Sandbox Code Playgroud)
String description = getValue(request.getPart("description")); // Retrieves <input type="text" name="description">
Run Code Online (Sandbox Code Playgroud)

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

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

提供上传的文件

有关将已保存的文件从磁盘或数据库正确提供回客户端的详细信息,请转到以下答案:

Ajax化表单

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


希望这一切都有帮助:)

  • @aaa:当您出于不清楚的原因使用“Reader”和/或“Writer”将字节转换为字符时,可能会发生这种情况。不要那样做。在读取和写入上传文件时,在所有位置使用“InputStream”/“OutputStream”,而不将字节转换为字符。PDF 文件不是基于字符的文本文件等。它是一个二进制文件。 (2认同)

Ami*_*ila 25

如果您碰巧使用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>
Run Code Online (Sandbox Code Playgroud)

在您的控制器中,将请求参数映射fileMultipartFiletype,如下所示:

@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
    }
}
Run Code Online (Sandbox Code Playgroud)

您可以使用MultipartFile's getOriginalFilename()和' 获取文件名和大小getSize().

我用Spring版本测试了这个4.1.1.RELEASE.


小智 12

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

要获取common.io.jar文件只是谷歌它或只是去Apache Tomcat网站,您可以在其中获得免费下载此文件的选项.但请记住一件事:如果您是Windows用户,请下载二进制ZIP文件.


fee*_*ing 9

我正在为每个 Html表单使用公共Servlet,无论它是否有附件.此Servlet返回一个TreeMap键,其中键是jsp名称参数和值是用户输入并将所有附件保存在固定目录中,稍后您重命名所选目录.Here 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;
    }
}
Run Code Online (Sandbox Code Playgroud)

  • 实时server中的任何目录.如果你编写了一个代码来在servlet中创建一个目录,那么将在live srver中创建目录 (2认同)

che*_*cho 8

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

web.xml文件中启用上载:

http://joseluisbz.wordpress.com/2014/01/17/manually-installing-php-tomcat-and-httpd-lounge/#Enabling%20File%20Uploads.

<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>
Run Code Online (Sandbox Code Playgroud)

你可以看到:

    <multipart-config>
      <max-file-size>3145728</max-file-size>
      <max-request-size>5242880</max-request-size>
    </multipart-config>
Run Code Online (Sandbox Code Playgroud)

使用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=""/>
Run Code Online (Sandbox Code Playgroud)

在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();
Run Code Online (Sandbox Code Playgroud)

编辑代码到servlet要求,例如max-file-size,max-request-size 和你可以设置的其他选项......


小智 8

对于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>
Run Code Online (Sandbox Code Playgroud)

控制器处理

@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");
    }
}
Run Code Online (Sandbox Code Playgroud)

希望能帮助到你 :)


小智 6

如果您使用Geronimo及其嵌入式Tomcat,则会出现此问题的另一个原因.在这种情况下,在测试commons-io和commons-fileupload的多次迭代之后,问题来自处理commons-xxx jar的父类加载器.必须防止这种情况.崩溃总是发生在:

fileItems = uploader.parseRequest(request);
Run Code Online (Sandbox Code Playgroud)

请注意,fileItems的List类型已更改为当前版本的commons-fileupload,List<FileItem>而不是通用版本的先前版本List.

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

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

Pasted from <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>
Run Code Online (Sandbox Code Playgroud)


归档时间:

查看次数:

538774 次

最近记录:

7 年,1 月 前