servlet获取form表单提交的文件

不知不觉,感觉我已经开始走向全栈的道路了,这两天学到了在后端接收前端传递过来的文件数据,由于水平太菜,我准备先使用servlet接收数据。
这篇博客由浅入深记录了我如何实现获取上传的文件、在html协议内容来看如何获取文件、我读Apache Commons FileUpload源码的一点理解。

How

html form

<form action="../form_ajax" method="post" enctype="multipart/form-data">
    用户名:<input type="text" name="username"/><br/>
    选择要上传的文件<br/>
    请选择文件:<input type="file" name="myfile"/><br/>
    <input type="submit" value="submit"/><br/>
</form>
  • 在form标签里面添加属性 enctype=”multipart/form-data”

用servlet api获取文件内容

@MultipartConfig
public class FileUpload1Servlet extends HttpServlet {

    private static Logger LOGGER = Logger.getLogger(FileUpload1Servlet.class);

    @Override
    protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        //文件上传
        //首先要在Servlet上用 @MultipartConfig 标识支持文件上传
        //获取part对象 参数为name属性的值
        Part part = req.getPart("myfile");
        //Servlet3没有提供直接获取文件名的方法,需要从请求头中解析出来
        //获取文件名
        String fileName = part.getSubmittedFileName();
        //获取数据的流
        InputStream inputStream = part.getInputStream();

        BufferedReader br = new BufferedReader(new InputStreamReader(inputStream));
        StringBuffer sb = new StringBuffer();
        String line = br.readLine();
        while(line != null){
            sb.append("\n").append(line);
            line = br.readLine();
        }
        LOGGER.info("content:"+sb.toString());

        PrintWriter printWriter = resp.getWriter();
        printWriter.print("皮皮虾,我们走。");
        printWriter.flush();
        printWriter.close();
    }

}

含有 enctype=”multipart/form-data” 属性的表单提交上来的数据,用ServletRequest.getParameter 方法是获取不到参数的。要按照下面的步骤获取参数:

  • 1、Servlet类要加上注释@MultipartConfig
  • 2、使用ServletRequest.getPart方法获取Part对象,Part对象就是对应的你的表单里面的每一个input。

缺点:

  • 如果我们想获取传递过来的表单里的value,Part只给我们提供了InputStream getInputStream() throws IOException;方法,对于普通的文本类型,我们还得自己处理。

用Apache Commons FileUpload获取文件内容

首先添加Apache Commons FileUpload依赖。

public class FileUpload2Servlet extends HttpServlet {

    private static Logger LOGGER = Logger.getLogger(FileUpload2Servlet.class);

    @Override
    protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {

        // Configure a repository (to ensure a secure temp location is used)
        ServletContext servletContext = this.getServletConfig().getServletContext();
        File repository = (File) servletContext.getAttribute("javax.servlet.context.tempdir");
        factory.setRepository(repository);

        // Create a new file upload handler
        ServletFileUpload upload = new ServletFileUpload(factory);

        try {
            // Parse the request
            List<FileItem> items = upload.parseRequest(req);

            // Process the uploaded items
            Iterator<FileItem> iter = items.iterator();
            while (iter.hasNext()) {
                FileItem item = iter.next();

                if (item.isFormField()) {
                    processFormField(item);
                } else {
                    processUploadedFile(item);
                }
            }
        } catch (Exception e) {
            e.printStackTrace();
        }

        PrintWriter printWriter = resp.getWriter();
        printWriter.print("皮皮虾,我们走。");
        printWriter.flush();
        printWriter.close();
    }

    private void processFormField(FileItem item) {
    }

    private void processUploadedFile(FileItem item) {
    }

}

如上面代码,获取到的FileItem就是对应的表单里的input,通过FileItem可以获取流或者值等,比原生的servlet api要方便。

Apache Commons FileUpload获取非file属性乱码

FileItem.getString是获取表单传递过来的属性值的方法,该方法默认是用 ISO-8859-1 解码的。该方法有一个重载的方法 String getString(String encoding) throws UnsupportedEncodingException; 来指定解码字符集。

注意

如果form表单里没有添加enctype=”multipart/form-data”(或者multipart/mixed等相似的)属性,我们就使用 ServletRequest.getParameter 来获取参数,不能使用上面的介绍的两种方式处理,否则会报错。

如果form表单里添加了enctype=”multipart/form-data”(或者multipart/mixed等相似的)属性,就需要使用上述两种方法或类似的方法处理请求参数,因为 ServletRequest.getParameter 方法无法获取到值。

因此,对于form表单里是否添加了enctype=”multipart/form-data”(或者multipart/mixed等相似的)属性,会有不同的处理。当然,这本来就是已知的,因为那form表单都是我们自己写的。我们也可以从传递过来的http请求头里判断出来,下面的博客中就会提现出来再http请求发送的数据里到底是如何不同的。Apache Commons FileUpload库给我们提供了简单的方法判断,如:

boolean isMultipart = ServletFileUpload.isMultipartContent(req);

why

socket实现简单web服务器,可查看http请求信息 这是我以前写的一篇博客,通过它可以直观的观察到http请求发送的内容。

enctype=”multipart/form-data”:这个属性影响的是当post数据的时候,是以一种什么样的形式来编码数据。该属性默认值是application/x-www-form-urlencoded。我们只讨论post,enctype为这两个值是的情况。

enctype=”application/x-www-form-urlencoded”

POST / HTTP/1.1
Host: 127.0.0.1:8000
Connection: keep-alive
Content-Length: 44
Cache-Control: max-age=0
Origin: http://localhost:8080
Upgrade-Insecure-Requests: 1
User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_11_6) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/57.0.2987.133 Safari/537.36
Content-Type: application/x-www-form-urlencoded
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8
Referer: http://localhost:8080/jquery/jquery_easy_ui_demo/index2.html?
Accept-Encoding: gzip, deflate, br
Accept-Language: zh-CN,zh;q=0.8,en;q=0.6
Cookie: _ga=GA1.1.2113425928.1460981708

username=%E5%BC%A0%E4%B8%89&myfile=test1.sql

enctype=”multipart/form-data”

POST / HTTP/1.1
Host: 127.0.0.1:8000
Connection: keep-alive
Content-Length: 1043
Cache-Control: max-age=0
Origin: http://localhost:8080
Upgrade-Insecure-Requests: 1
User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_11_6) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/57.0.2987.133 Safari/537.36
Content-Type: multipart/form-data; boundary=----WebKitFormBoundary2vmXA3pgPdCoiZjv
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8
Referer: http://localhost:8080/jquery/jquery_easy_ui_demo/index2.html?
Accept-Encoding: gzip, deflate, br
Accept-Language: zh-CN,zh;q=0.8,en;q=0.6
Cookie: _ga=GA1.1.2113425928.1460981708


------WebKitFormBoundary2vmXA3pgPdCoiZjv
Content-Disposition: form-data; name="username"

张三
------WebKitFormBoundary2vmXA3pgPdCoiZjv
Content-Disposition: form-data; name="myfile"; filename="test1.sql"
Content-Type: application/octet-stream

/*
 Navicat MySQL Data Transfer

 Source Server         : localhost
 Source Server Version : 50716
 Source Host           : localhost
 Source Database       : test1

 Target Server Version : 50716
 File Encoding         : utf-8

 Date: 11/18/2016 00:54:30 AM
*/

SET NAMES utf8;
SET FOREIGN_KEY_CHECKS = 0;

-- ----------------------------
--  Table structure for `obj1`
-- ----------------------------
DROP TABLE IF EXISTS `obj1`;
CREATE TABLE `obj1` (
  `name` varchar(20) NOT NULL,
  `id` int(11) NOT NULL,
  PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;

-- ----------------------------
--  Records of `obj1`
-- ----------------------------
BEGIN;
INSERT INTO `obj1` VALUES ('张三', '1');
COMMIT;

SET FOREIGN_KEY_CHECKS = 1;

------WebKitFormBoundary2vmXA3pgPdCoiZjv--

区别

在这里我们只需要注意两个地方:请求头里的Content-Type属性和请求体。

  • 请求头里的Content-Type属性属性不同,服务器端可以从这个属性判断用那种方式去处理请求参数。当Content-Type为multipart/form-data时,会有boundary=—-WebKitFormBoundary2vmXA3pgPdCoiZjv这么一个值,它就是区分请求体里不同的属性的。
  • enctype=”application/x-www-form-urlencoded”时,请求体里就是urlencoded过拼接二层的查询字符串。当enctype=”multipart/form-data”时,表单被boundary分成不同的部分,每一部分都包含header和数据,如上面的例子一样。

解析

当get请求或post且enctype=”application/x-www-form-urlencoded”时,直接使用ServletRequest.getParameter方法就能得到参数,这里就不讨论了。

当post且enctype=”multipart/form-data”时,数据就在请求体里,我们也知道请求体的格式了,就是按照这个格式把需要的数据解析出来。首先得到请求体力的内容,我们知道ServletRequest.getInputStream方法,该方法返回的输入流包含整个请求体力的内容。

对于真正解析上述的post请求体数据,还是比较复杂的一件事情,本着负责任(懒)的态度,我就没有自己动手写,下面内容是我阅读Apache Commons FileUpload源码的一点理解。

Apache Commons FileUpload理解

代码有点多,就不贴了,写点我的理解。

  • 1、把请求体先解析成FileItemIterator,该对象是一个迭代器,可以获取所有的FileItemStream对象,FileItemStream对象可以对应一个传递过来的input的内容,可以获取fieldName、流等数据。
  • 2、遍历FileItemIterator,通过FileItemStream生成FileItem,真正生成的是FileItem的子类DiskFileItem。
  • 3、解析时使用这样两层的结构为了方便拓展,FileItemStream对象能代表原始的数据,通过它生成FileItem,现在生成的是DiskFileItem,以后可以通过数据库缓存的FileItem也未可知。官方文档里也有介绍说使用FileItemStream获取数据,叫Streaming API.
  • 4、为了节省内存,DiskFileItem会根据它包含内容的大小决定是否把它包含的数据缓存在文件系统里。如下:
➜  jquery pwd
/Users/mabinbin/Library/Caches/IntelliJIdea2016.1/tomcat/Unnamed_blog_backend/work/Catalina/localhost/jquery
➜  jquery ls -lh
total 249792
-rw-r--r--  1 mabinbin  staff   122M  4 26 14:18 upload_08e39bf7_614a_4684_aee1_6d44138ff8f4_00000001.tmp

参考

HTML <form> 标签的 enctype 属性
socket实现简单web服务器,可查看http请求信息
FileUpload – Using FileUpload

  • 15
    点赞
  • 24
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
### 回答1: 您好,要将提交Servlet404,您需要在的action属性中指定Servlet的URL地址,例如: <form action="/servlet404" method="post"> <!-- 内容 --> </form> 这样,当用户提交时,数据将被发送到指定的Servlet404中进行处理。在Servlet中,您可以使用request对象来获取数据,例如: String username = request.getParameter("username"); String password = request.getParameter("password"); 然后您可以根据需要对数据进行处理和响应。希望这能帮助您! ### 回答2: 当我们在HTML页面中使用<form>标签创建,并在其中添加输入元素和提交按钮时,我们可以将其提交到一个指定的目标资源。在我们想要将数据提交到一个Servlet的情况下,我们需要在<form>标签的action属性中指定Servlet的路径。 但是,在一些情况下,我们可能会遇到"Servlet 404 Not Found"的错误,这意味着服务器无法找到我们指定的Servlet。有几个可能的原因会导致这个错误: 1. Servlet路径指定不正确:如果我们在action属性中指定了错误的Servlet路径,服务器就会无法找到该Servlet。因此,我们需要确保指定的路径是正确的,并且在服务器中存在。 2. Servlet没有正确的映射:即使我们指定了正确的Servlet路径,如果Servlet没有正确地映射到服务器中的某个目录或文件,服务器也不会找到它。我们需要确保已正确地配置Servlet的映射。 3. 应用程序上下文路径不正确:如果我们的Servlet位于Web应用程序的子目录中,则我们需要将应用程序的上下文路径包括在Servlet路径中,以便服务器正确地找到它。 4. 服务器文件丢失:如果Servlet文件已被删除或移动到其他位置,则服务器将无法找到它。我们需要确保Servlet文件位于正确的位置,并且已复制到服务器的正确目录中。 5. 服务器未正确配置:最终可能性是服务器本身的配置有问题,这可能导致无法正确查找Servlet。在这种情况下,我们需要检查服务器设置,并确保已正确地安装和配置。 总之,当我们遇到"Servlet 404 Not Found"错误时,需要检查我们的代码和服务器配置,以确保我们已正确地指定了Servlet路径,并且Servlet已正确地映射到服务器上的某个目录或文件。 ### 回答3: form提交servlet404,可能是出现了以下几种情况: 1. servlet的URL链接没有正确设置,导致无法找到对应的servlet页面。这种情况下,我们需要对代码中的servlet路径进行检查,找出问题所在,并进行调整。 2. 服务器端的配置不正确,导致无法处理请求。这种情况下,我们需要对服务器端的配置进行检查,如检查web.xml文件中的ServletContext配置是否正确设置。 3. servlet中的路由匹配有误,导致无法处理请求。这种情况下,我们需要检查servlet代码中的路径匹配逻辑是否正确,如路径名是否大小写敏感。 4. servlet或web应用程序中的权限设置不正确,导致无法处理请求。这种情况下,我们需要对权限设置进行检查,如是否拥有访问servlet的权限、是否具有目标路径的读取/写入权限等。 总之,form提交servlet404一般情况下是因为路径不正确或者权限设置不正确,只要检查以上几点,就可以解决问题,确保form顺利提交servlet并获得正确的响应。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值