Java Web文件上传原理分析(不借助开源fileupload上传jar包)
这一问,我楞了。现在开源MVC框架满天飞,大部分的框架像Struts2都会自动的把你上传的文件封装成Action的属性。几乎没有人自己去分析请求流数据,然后获取文件数据的。
总结下文件上传的几种实现:
      
一、像Struts2这样的框架自动将你上传的文件封装到Action的属性中,在配置文件里面你只需要指定文件上传的临时目录即可。
二、借助Apache的common-fileupload组件,实现文件上传。让开源组件帮你自动分析请求流数据,核心代码如下,详细的请参考
      http://morfil.iteye.com/blog/66945:
public void doPost(HttpServletRequest request,HttpServletResponse response) throws IOException, ServletException
{
try {
DiskFileUpload fu = new DiskFileUpload();
// 设置最大文件尺寸,这里是4MB
fu.setSizeMax(4194304);
// 设置缓冲区大小,这里是4kb
fu.setSizeThreshold(4096);
// 设置临时目录:
fu.setRepositoryPath(tempPath); // 得到所有的文件:
List fileItems = fu.parseRequest(request);
Iterator i = fileItems.iterator();
// 依次处理每一个文件:
while(i.hasNext()) {
FileItem fi = (FileItem)i.next();
// 获得文件名,这个文件名包括路径:
String fileName = fi.getName();
// 在这里可以记录用户和文件信息
// ...
// 写入文件,暂定文件名为a.txt,可以从fileName中提取文件名:
fi.write(new File(uploadPath + "a.txt"));
}
}
catch(Exception e) {
// 可以跳转出错页面
}
}
三、Servlet 3.0通过@MultipartConfig(location =
"/home/yongboy/tmp/", maxFileSize = 1024 * 1024 *
10)和@WebServlet("/upload")注解也可以实现文件上传,核心代码如下,详情请见
      http://www.blogjava.net/yongboy/archive/2011/01/15/346202.html:
/**
* 上传文件测试 location为临时文件保存路径
*
* @author yongboy
* @date 2011-1-13
* @version 1.0
*/
@MultipartConfig(location = "/home/yongboy/tmp/", maxFileSize = 1024 * 1024 * 10)
@WebServlet("/upload")
public class UploadFileAction extends HttpServlet {
private static final long serialVersionUID = 92166165626L;
private static final Log log = LogFactory.getLog(UploadFileAction.class);
// 得到注解信息
private static final MultipartConfig config; static {
config = UploadFileAction.class.getAnnotation(MultipartConfig.class);
} protected void doGet(HttpServletRequest request,
HttpServletResponse response) throws ServletException, IOException {
request.getRequestDispatcher("/upload.jsp").forward(request, response);
} protected void doPost(HttpServletRequest request,
HttpServletResponse response) throws ServletException, IOException {
// 为避免获取文件名称时出现乱码
request.setCharacterEncoding("UTF-8"); Part part = null;
try {
// <input name="file" size="50" type="file" />
part = request.getPart("file");
} catch (IllegalStateException ise) {
// 上传文件超过注解所标注的maxRequestSize或maxFileSize值
if (config.maxRequestSize() == -1L) {
log.info("the Part in the request is larger than maxFileSize");
} else if (config.maxFileSize() == -1L) {
log.info("the request body is larger than maxRequestSize");
} else {
log.info("the request body is larger than maxRequestSize, or any Part in the request is larger than maxFileSize");
} forwardErrorPage(request, response, "上传文件过大,请检查输入是否有误!");
return;
} catch (IOException ieo) {
// 在接收数据时出现问题
log.error("I/O error occurred during the retrieval of the requested Part");
} catch (Exception e) {
log.error(e.toString());
e.printStackTrace();
} if (part == null) {
forwardErrorPage(request, response, "上传文件出现异常,请检查输入是否有误!");
return;
} // 得到文件的原始名称,eg :测试文档.pdf
String fileName = UploadUtils.getFileName(part); log.info("contentType : " + part.getContentType());
log.info("fileName : " + fileName);
log.info("fileSize : " + part.getSize());
log.info("header names : ");
for (String headerName : part.getHeaderNames()) {
log.info(headerName + " : " + part.getHeader(headerName));
} String saveName = System.currentTimeMillis() + "."
+ FilenameUtils.getExtension(fileName); log.info("save the file with new name : " + saveName); // 因在注解中指定了路径,这里可以指定要写入的文件名
// 在未执行write方法之前,将会在注解指定location路径下生成一临时文件
part.write(saveName); request.setAttribute("fileName", fileName);
request.getRequestDispatcher("/uploadResult.jsp").forward(request,
response);
} private void forwardErrorPage(HttpServletRequest request,
HttpServletResponse response, String errMsg)
throws ServletException, IOException {
request.setAttribute("errMsg", errMsg); request.getRequestDispatcher("/upload.jsp").forward(request, response);
}
}
获取上传文件名:
/**
* 如何得到上传的文件名, API没有提供直接的方法,只能从content-disposition属性中获取
*
* @param part
* @return
*/
protected static String getFileName(Part part) {
if (part == null)
return null; String fileName = part.getHeader("content-disposition");
if (StringUtils.isBlank(fileName)) {
return null;
} return StringUtils.substringBetween(fileName, "filename=\"", "\"");
}
四、就是自己去解析请求流数据,当然现在没有人会傻到还去自己做这种事情。这里我们只是了解下Apache的那帮牛人是怎么做到文件上传的。
首先我们先分析文件上传HTTP请求头:
一、文件上传Http请求Request截图:
      
 
      
      
 
      贴个文字版的,里面的数据我使用谷歌浏览器截获的,方便大家拷贝
      
Request URL:http://127.0.0.1/dbm2-web/flowmgmt/FlowMgmtAction/uploadFile.go
      
Request Method:POST
      
Status Code:200 OK
      
 
      Request Headers             view source
      
   Accept:text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8
      
   Accept-Charset:GBK,utf-8;q=0.7,*;q=0.3
      
   Accept-Encoding:gzip,deflate,sdch
      
   Accept-Language:zh-CN,zh;q=0.8
      
   Cache-Control:max-age=0
      
   Connection:keep-alive
      
   Content-Length:54738
      
   Content-Type:multipart/form-data; boundary=----      WebKitFormBoundaryhQslmBE7nbTLTJzD
      
   Cookie:JSESSIONID=1ge6jxgy4s166bzms8og4j7us
      
   Host:127.0.0.1
      
   Origin:http://127.0.0.1
      
   Referer:http://127.0.0.1/dbm2-web/page/modules/flowmgmt/main.jsp
      
   User-Agent:Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.1 (KHTML, like Gecko)    Chrome/21.0.1163.0 Safari/537.1
      
 
      Request Payload
      
   ------WebKitFormBoundaryhQslmBE7nbTLTJzD
      
   Content-Disposition: form-data; name="field1"; filename="exportFlow-4113.fd"
      
   Content-Type: application/octet-stream
------WebKitFormBoundaryhQslmBE7nbTLTJzD--
      
 
      Response Headers             view source
      
   Expires:Thu, 01-Jan-1970 00:00:00 GMT
      
   Server:Jetty(7.3.1.v20110307)
      
   Set-Cookie:JSESSIONID=oyklz60lg9aq1dn127p62ki0t;Path=/dbm2-web
      
   Transfer-Encoding:chunked
从上述结构上我们可以知道,文件上传的Content-Type为multipart/form-data;
boundary=----WebKitFormBoundaryhQslmBE7nbTLTJzD,而普通的form表单的Content-Type为application/x-www-form-urlencoded。因此,我们可以利用HttpServletRequest的request.getHeaderNames()方法和request.getHeaders(headName)方法得到请求头Headers中的Content-Type数据,然后根据Content-Type数据中是否包含multipart/form-data来区分请求是否为文件上传请求。其中boundary为文件数据的分隔符,用于区分上传多个文件。详细介绍请见http://yefeng.iteye.com/blog/315847
了解请求头的结构后,我们看看如何用程序去得到普通请求和文件上传请求的数据
      
 
      http://blog.csdn.net/liuyar/article/details/6202331
二、文件上传Http请求Response截图:
      
 
      
三、文件上传Http请求Cookies截图:
      
 
      
     
Java Web文件上传原理分析(不借助开源fileupload上传jar包)的更多相关文章
- Java Web文件上传
		
参考资料:http://www.cnblogs.com/xdp-gacl/p/4200090.html 一.问题描述 Java Web文件上传需要借助一些第三方库,常用的是借助Apache的包,有两个 ...
 - Java web文件上传下载
		
[版权申明:本文系作者原创,转载请注明出处] 文章出处:http://blog.csdn.net/sdksdk0/article/details/52048666 作者:朱培 ID:sdksdk0 邮 ...
 - Junit 注解  类加载器 .动态代理 jdbc 连接池  DButils 事务 Arraylist Linklist hashset 异常 哈希表的数据结构,存储过程 Map Object String Stringbufere File类  文件过滤器_原理分析 flush方法和close方法 序列号冲突问题
		
Junit 注解 3).其它注意事项: 1).@Test运行的方法,不能有形参: 2).@Test运行的方法,不能有返回值: 3).@Test运行的方法,不能是静态方法: 4).在一个类中,可以同时定 ...
 - java并发包&线程池原理分析&锁的深度化
		
java并发包&线程池原理分析&锁的深度化 并发包 同步容器类 Vector与ArrayList区别 1.ArrayList是最常用的List实现类,内部是通过数组实现的, ...
 - java web 文件上传下载
		
文件上传下载案例: 首先是此案例工程的目录结构:
 - Java WEB ----- 文件的上传
		
最近学到的web阶段的文件的上传,就想记录一下,帮助自己复习以及帮助大家学习,一般我都会把上传的文件存到服务器中的web-inf 下面,因为这样用户不会直接访问到,我们存到数据库的一般都是路径.这里没 ...
 - [转]java web 文件上传
		
实现WEB开发中的文件上传功能,需完成如下二步操作: 在WEB页面中添加上传输入项,<input type=“life” name=“”>,使用时注意: 1. 必须要设置 ...
 - java web文件上传功能实现
		
核心原理: 该项目核心就是文件分块上传.前后端要高度配合,需要双方约定好一些数据,才能完成大文件分块,我们在项目中要重点解决的以下问题. * 如何分片: * 如何合成一个文件: * 中断了从哪个分片开 ...
 - Java 远程通讯技术及原理分析
		
在分布式服务框架中,一个最基础的问题就是远程服务是怎么通讯的,在Java领域中有很多可实现远程通讯的技术,例如:RMI.MINA.ESB.Burlap.Hessian.SOAP.EJB和JMS等,这些 ...
 
随机推荐
- string 模块
			
string 说明:string 模块保留了很多有用的常量和类,用来处理 string 和 unicode 对象. 作用:包含处理文本的常量和类. capwords( ) capwords( ) 的作 ...
 - python 时间time模块介绍和应用
			
1.其中format_string 类型的时间和struct_time之间可以转换,timestamp时间戳可以和struct_time之间进行转化,但是时间戳和格式化时间是不能直接转换的. time ...
 - python小项目之微信远程控制
			
前两天接触了一个有趣的python模块--itchat,这个模块可以非常方便的操作微信,今天就来使用这个模块来实现微信远程控制. 环境准备 itchat模块不是python标准模块(内置模块),是一个 ...
 - 怎样在windows上定时执行python脚本
			
作为一个需要在电脑上工作和学习的人,一件十分困扰我的事情就是怎样不受互联网中其他内容的干扰而专注于自己想要做的事情,有的时候真的是沉浸于微博上的消息,忘了自己本来想要做的事.不过我有一件神器,自己爱豆 ...
 - 用k8s构建生产环境下应用服务
			
1.生成镜像 见https://www.cnblogs.com/mushou/p/9713741.html,把测试成熟的应用添加到tomcat镜像生成新的镜像,用ansible部署到集群的几点服务器中 ...
 - Python2+python3——多版本启动和多版本pip install问题
			
背景描述: python2版本都知道维护到2020年,目前使用python的很大一部分用户群体都开始改安装并且使用最新版的python3版本了,python2和python3在编程大的层面不曾改变,有 ...
 - bat脚本,winscp,shell加mysql存储过程实现mysql单条数据迁移
			
起因 公司有个任务,需要迁移mysql中的单条数据.从公司的dev环境到staging环境,dev环境的mysql安装在windows server 2012 R2下,stage是aws的服务器不能直 ...
 - axios的post请求后台(ThinkPHP5)接收不到数据
			
最近做vue项目,做分页的功能,使用post给后台发送数据,使用接口还是工具(postman)都可获取数据,唯独axios获取不到:经过排除,发现这与axios的post传参格式有关系: this.$ ...
 - Go 学习之路:异常处理defer,panic,recover
			
Go没有像Java那样的异常机制,它不能抛出异常.因为设计者们认为,将异常与控制结构混在一起容易使得代码变得混乱.于是乎引入Exception处理: defer,panic,recover; 简单描述 ...
 - [Golang学习笔记] 05 程序实体2 作用域访问权限和变量重声明
			
作用域访问权限: 程序实体访问权限(作用域)有三种:1. 包级私有(代码包)2. 模块级私有(代码包)3. 公开(全域). 一个函数是一个代码块.一个程序实体的作用域总是会被限制在某个代码块中.好处: ...