其实文件上传的文章已经写得很多了,但是好多文章都是都是说明了怎么实现,没有说这个过程到底发生了什么(会不会引来仇恨。。),其实实现文件上传并不复杂,也没有多少代码,但是要是清楚的明白其中的原理还是费点功夫的,这里就还原文件上传的整个过程。

其实关于文件上传在最早之前是使用Apache的Commons FileUpload组件,但是自从servlet提出了自己的解决办法之后,就不再使用这个组件了,有了正规军谁还使用民兵啊,不对,也不一定,之前Apache的HttpClient就比JDK自己的HttpUrlConnection流行,不说废话了,直接进入!

一、 客户端编程

下面是我们的页面FileUpload.jsp
<%@ page language="java" contentType="text/html; charset=ISO-8859-1"pageEncoding="ISO-8859-1"%>
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=ISO-8859-1">
<title>File upload</title>
</head>
<body><form action="/JavaServlet/fileUploadServlet" method="post" enctype="multipart/form-data">select a file :<input type="file" name="file" multiple><input type="text" value="upload file" name="identifier" /><input type="submit" value="upload" /></form>
</body>
</html>

关于这个页面有几点值得注意:

  • 首先是action是URI,注意它和URL的区别,不要省掉contextPath,这样会找不到该资源的;
  • enctype的值一定是multipart/form-data,这个属性是指在发送放到服务器之前如何对表单数据进行编码,这种方式是将表单数据组装成一条消息,并用分隔符将表单的每个部分分隔开;默认值是application/x-www-form-urlencoded,也意味着所有的值都会进行编码,这种方式是用键值对来进行编码;
  • 如果想要上传多个文件,可以使用multiple属性,注意这个属性是在HTML5中提出的,这样就不用我们使用多个input来上传多个文件了;

下面我上传三个文件,可以看到Http请求和响应如下,我使用的是chrome浏览器:

这里边最重要的就是Content-type,这个类型和表单的enctype类型相同,最重要的就是增加了boundary属性,该属性的值就是用来分割表单中各个部分的。
下面是post表单时发出的request payload,如下:
从图中可以看出整个被上传的表单数据是被分隔符包裹起来,并且通过使用"分隔符--"的方式来标明数据结束,这个分隔符在开头和结尾必须有又来说明数据的开始和结束,只有在表单中有多个元素或者上传多个文件时才会在中间出现,每一个被分隔符分隔的部分里面都包含Content-disposition首部,里面包含表单元素中的一些属性,有name,filename;但是content-type首部是可选的,而且对于表单中非文件的部分是没有content-type的,只有文件域才会有content-type这个首部
值得注意的是,当没有文件上传的时候仍然会有文件部分存在,只不过filename属性为空,如下:

二、 服务端编程

了解客户端是为了我们在服务端解析客户端发过来的请求,那么如何判断发过来的请求中是否包含文件呢?基于以下几点可以进行判断:
  • 在一个由multipart/form-data组成的请求中,每一个部分包括非文件部分都会转换成一个Part对象,在服务器端我们主要是针对该Part对象进行处理;
  • 通过查看Part中是否存在content-type首部来判断一个Part是属于普通的非文件部分,还是属于文件部分;
  • 如果存在content-type,则说明文件部分存在,之后查看上传的文件名称是否为空,文件名为空说明有客户端没有选择要上传的文件;
  • 如果文件存在,就使用Part的write方法来将他写入服务器端的文件系统;
在服务器上处理文件上传的servlet如下:
@WebServlet(name="fileUploadServlet", urlPatterns={"/fileUploadServlet"})
//@MultipartConfig(location="/")
@MultipartConfig
public class FileUploadServlet extends HttpServlet {private static final long serialVersionUID = 1920423365061691218L;@Overrideprotected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {Collection<Part> parts = req.getParts();for(Part part: parts){if(part.getContentType() != null){String filename = getFileName(part);if(filename != null && !filename.isEmpty()){part.write(filename);}} }}String getFileName(Part part){Objects.requireNonNull(part, "part can not be null");String disposition = part.getHeader("content-disposition");String[] disParts = disposition.split(";");String filenamePart = disParts[disParts.length - 1];String filename = filenamePart.substring(filenamePart.indexOf("=")+1).trim().replace("\"", "");return filename;}
}

关于上述的处理其实主要围绕@MultipartConfig注解和Part接口来进行,关于这两个的使用其实很简单,可以查看一下JavaDoc即可,但是有两个我要着重说一下,因为我自己就掉坑里了:

  • 一个就是@MultipartConfig中的location属性,这个绝对是一个坑,当这个值是一个绝对路径时,调用Part的write()方法将该文件写到对应的路径是没有问题的,但是当是相对路径的时候,比如如我上边写的"/",这个相对路径是相对于tomcat路径下的C:\Program Files\tomcat7\work\Catalina\localhost\JavaServlet路径的,这是一个文件上传临时保存的位置,这个路径值主要是为了在文件超过预设大小时写入硬盘,为@MultiSizeThreshold准备,所以最好还是不要使用这个属性为好;
  • 还有一个就是Part中的getName()方法并不是用来获取文件名的,而是用来获取表单元素中的name属性的;文件名需要我们自己来解析出来;
  • 在使用Part的write()方法时,如果提供的是相对路径,那么相对路径的根路径都是C:\Program Files\tomcat7\work\Catalina\localhost\JavaServlet;

三、 其他问题

上面两个部分主要就是说明了客户端和服务端分别怎么做,但是根据具体的业务逻辑还有很多别的需求需要考虑,如下:
  • 对文件的后缀名进行验证和约束;
  • 对文件的大小进行约束;
  • 文件的存储,是放在本地文件系统上还是数据库中;
  • 文件的编码问题,尤其是中文的编码问题;
  • 避免相同文件名的文件的重复上传导致覆盖问题;
这些问题实现起来其实都比较简单,下面有一篇文章可以进行参考,主要是怕考虑不到这些问题,或者为了省事偷工减料。
相关文章:
  • 表单提交时编码类型enctype详解
  • Returning Values from Forms: multipart/form-data
  • Tomcat根目录下work文件夹的作用
  • The @MultipartConfig Annotation
  • Java web-文件上传
  • What's the difference between “Request Payload” vs “Form Data” as seen in Chrome dev tools Network tab

Java Web基础知识之文件上传:文件上传一窥究竟相关推荐

  1. java WEB 基础复习_开篇--Java Web基础知识

    本篇主要是博客的开篇,是一些很基础的知识,我写博客的目的很简单.主要是供自己学习使用,督促自己学习,希望自己能坚持下来. 当前三大主流的Web动态网页技术是PHP(Personal Home Page ...

  2. Java Web基础知识之安全:人生苦短,注意安全

    关于web程序中的安全方面,想必大多数人都不甚了解,或者说感觉没有必要了解,身边开发网站的人主要就是注重后台的功能和前台的界面,不要说程序的安全问题,甚至后台数据库访问的问题可能都没有下大力气解决.但 ...

  3. java培训基础知识都学哪些

    很多人都开始学习java技术,觉得java语言在未来的发展前景空间非常大,事实却是如此,那么针对于零基础的同学, 学习java技术需要学哪些呢?下面我们就来看看java培训基础知识都学哪些? java ...

  4. Java面试基础知识III

    Java面试基础知识: 1.C++或Java中的异常处理机制的简单原理和应用. 当JAVA 程序违反了JAVA的语义规则时,JAVA虚拟机就会将发生的错误表示为一个异常.违反语义规则包括2种情况.一种 ...

  5. Java Web基础

    目录 Java Web基础 HTML基础 HTML补充 CSS基础 CSS补充 JavaScript基础 JavaScript补充(例题) Java Web补充 Java Web基础 HTML基础 标 ...

  6. 【笔记】网易微专业-Web安全工程师-01.WEB基础知识

    课程概述: 本课是基础中的基础,通俗易懂的讲解了Web的本质和Web开发的基础知识.对于Web小白,建议从头开始抓紧学习:对于已经有一定Web基础知识的同学,建议快速的过一遍,夯实基础. 课程大纲: ...

  7. Java进阶基础知识

    Java进阶基础知识 1.Java 基础 Java类设计的原则就是内聚性,一致性和封装性是Java设计的基本原则 1.1 Java基础理论 Java基础理论知识 1.2继承的优缺点 1.2.1优点 : ...

  8. 你觉得什么才是 Java 的基础知识?

    近日里,很多人邀请我回答各种j2ee开发的初级问题,我无一都强调java初学者要先扎实自己的基础知识,那什么才是java的基础知识?又怎么样才算掌握了java的基础知识呢?这个问题还真值得仔细思考. ...

  9. java ee基础知识_Java EE:基础知识

    java ee基础知识 想要了解一些基本原则,即与Java EE相关的技术术语. 对于许多人来说,Java EE / J2EE仍然最多意味着Servlet,JSP或Struts. 没有冒犯或双关语! ...

  10. hashcode是什么意思_什么才是 Java 的基础知识?

    作者:晓风轻 链接:zhuanlan.zhihu.com/p/28615617 近日里,很多人邀请我回答各种j2ee开发的初级问题,我无一都强调java初学者要先扎实自己的基础知识,那什么才是java ...

最新文章

  1. IPS与IDS部署场景(直路部署,单臂部署,旁路部署,阻断)
  2. 套娃的开始——Network in Network网络学习笔记
  3. mac上的命令行工具
  4. 运行maven项目整合ssm时的错误笔记
  5. 计算机六级好考吗,计算机六级考什么?
  6. 牛客网暑期ACM多校训练营(第三场): A. Ternary String(欧拉降幂+递推)
  7. Nacos概述,下载与安装,初始化配置,服务注册应用,RestTemplate,Feign
  8. vip会员统计表 (vip等级是灵活配置的 非写死1是金卡用户 2是什么 等)
  9. cognos ibm 收购_IBM Cognos与Linux上的Sterling Selling and Fulfillment Suite集成
  10. 为什么说Mobileye最辉煌的5年就是落后的5年
  11. zune自搭虚拟服务器离线升级,Zune 30g 购买/使用 个人经验贴,希望可以帮到有需要的烧友~...
  12. 服务器重装Linux系统详细图文教程(centos7)
  13. LNK1104:无法打开文件d3dx11.lib
  14. 华大460 GPIO 例程赏析_20220911
  15. 基于javaagent-ByteBuddy监控方法执行耗时
  16. Oracle 分组数据
  17. C# 读取txt文件生成Word文档
  18. [LeetCode]Medium - Cutting Ribbons - python
  19. nc文件的读取与可视化-python
  20. Arduino 无源蜂鸣器警报声

热门文章

  1. 辞旧迎新又一年(18年年终总结)
  2. HTML两张图片翻转,canvas实现图片镜像翻转的2种方式
  3. R语言详解参数检验和非参数检验——样本T检验、方差分析、pearson相关性检验、单样本wilcoxon检验、Mann-Whitney检验、配对样本wilcoxon检验、列联表检验、卡方检验
  4. 关于LaTex输入大写罗马字母的命令——\expandafter的理解与\MakeUppercase的使用
  5. 五分钟告诉你什么是爬虫?
  6. Python与爬虫有什么关系?
  7. linux 屏幕录像软件,Linux_Linux下好用的5个屏幕录像软件,在很多时候,我们需要将在 Lin - phpStudy...
  8. 系统自带恶意软件清理助手
  9. 【酷毙了】野火新版fireTools多功能调试助手,有Windows和Linux版本,就问你喜不喜欢。...
  10. Clonezilla再生龙备份系统分区详细教程