JavaEE系列之(二)commons-fileupload实现文件上传、下载
一、文件上传概述
<form action="#" method="post" enctype="multipart/form-data">
<input type="file" name="filename1"/><br>
<input type="file" name="filename2"/><br>
<input type="submit" value="上传"/>
<form>
<!-- 1、表单方式必须是post
2、必须设置encType属性为 multipart/form-data.设置该值后,浏览器在上传文件时,将会把文件数据附带在http请求消息体中,
并使用MIME协议对上传的文件进行描述,以方便接收方对上传数据进行解析和处理。
3、必须要设置input的name属性,否则浏览器将不会发送上传文件的数据。
-->2、在Servlet中读取文件上传数据,并保存到服务器硬盘
Accept-Language: zh-Hans-CN,zh-Hans;q=0.5
Content-Type: multipart/form-data; boundary=---------------------------7dfa01d1908a4
UA-CPU: AMD64
Accept-Encoding: gzip, deflate
User-Agent: Mozilla/5.0 (Windows NT 6.2; Win64; x64; Trident/7.0; rv:11.0) like Gecko
Content-Length: 653
Host: localhost:8080
Connection: Keep-Alive
Pragma: no-cache
Cookie: JSESSIONID=11CEFF8E271AB62CE676B5A87B746B5F
-----------------------------7dfa01d1908a4
Content-Disposition: form-data; name="username"
zhangsan
-----------------------------7dfa01d1908a4
Content-Disposition: form-data; name="userpass"
1234
-----------------------------7dfa01d1908a4
Content-Disposition: form-data; name="filename1"; filename="C:\Users\ASUS\Desktop\upload.txt"
Content-Type: text/plain
this is first file content!
-----------------------------7dfa01d1908a4
Content-Disposition: form-data; name="filename1"; filename="C:\Users\ASUS\Desktop\upload2.txt"
Content-Type: text/plain
this is Second file content!
hello
-----------------------------7dfa01d1908a4--
从上面的数据中也可以看出,如果自己手工的去分割读取数据很难写出健壮稳定的程序。所以,为方便用户处理上传数据,Apache开源组织提供了一个用来处理表单文件上传的一个开源组件(Commons-fileupload),该组件性能优异,并且其API使用极其简单,可以让开发人员轻松实现web文件上传功能,因此在web开发中实现文件上传功能,通常使用Commons-fileupload组件实现。
response.setContentType("text/html;charset=utf-8");//设置响应编码
request.setCharacterEncoding("utf-8");
PrintWriter writer = response.getWriter();//获取响应输出流 ServletInputStream inputStream = request.getInputStream();//获取请求输入流 /*
* 1、创建DiskFileItemFactory对象,设置缓冲区大小和临时文件目录
* 该类有两个构造方法一个是无参的构造方法,
* 另一个是带两个参数的构造方法
* @param int sizeThreshold,该参数设置内存缓冲区的大小,默认值为10K。当上传文件大于缓冲区大小时,fileupload组件将使用临时文件缓存上传文件
* @param java.io.File repository,该参数指定临时文件目录,默认值为System.getProperty("java.io.tmpdir");
*
* 如果使用了无参的构造方法,则使用setSizeThreshold(int sizeThreshold),setRepository(java.io.File repository)
* 方法手动进行设置
*/
DiskFileItemFactory factory = new DiskFileItemFactory(); int sizeThreshold=1024*1024;
factory.setSizeThreshold(sizeThreshold); File repository = new File(request.getSession().getServletContext().getRealPath("temp"));
// System.out.println(request.getSession().getServletContext().getRealPath("temp"));
// System.out.println(request.getRealPath("temp"));
factory.setRepository(repository); /*
* 2、使用DiskFileItemFactory对象创建ServletFileUpload对象,并设置上传文件的大小
*
* ServletFileUpload对象负责处理上传的文件数据,并将表单中每个输入项封装成一个FileItem
* 该对象的常用方法有:
* boolean isMultipartContent(request);判断上传表单是否为multipart/form-data类型
* List parseRequest(request);解析request对象,并把表单中的每一个输入项包装成一个fileItem 对象,并返回一个保存了所有FileItem的list集合
* void setFileSizeMax(long filesizeMax);设置单个上传文件的最大值
* void setSizeMax(long sizeMax);设置上传温江总量的最大值
* void setHeaderEncoding();设置编码格式,解决上传文件名乱码问题
*/
ServletFileUpload upload = new ServletFileUpload(factory); upload.setHeaderEncoding("utf-8");//设置编码格式,解决上传文件名乱码问题
/*
* 3、调用ServletFileUpload.parseRequest方法解析request对象,得到一个保存了所有上传内容的List对象
*/
List<FileItem> parseRequest=null;
try {
parseRequest = upload.parseRequest(request);
} catch (FileUploadException e) {
e.printStackTrace();
}
/*
* 4、对list进行迭代,每迭代一个FileItem对象,调用其isFormField方法判断是否是文件上传
* true表示是普通表单字段,则调用getFieldName、getString方法得到字段名和字段值
* false为上传文件,则调用getInputStream方法得到数据输入流,从而读取上传数据
*
* FileItem用来表示文件上传表单中的一个上传文件对象或者普通的表单对象
* 该对象常用方法有:
* boolean isFormField();判断FileItem是一个文件上传对象还是普通表单对象
* true表示是普通表单字段,
* 则调用getFieldName、getString方法得到字段名和字段值
* false为上传文件,
* 则调用getName()获得上传文件的文件名,注意:有些浏览器会携带客户端路径,需要自己减除
* 调用getInputStream()方法得到数据输入流,从而读取上传数据
* delete(); 表示在关闭FileItem输入流后,删除临时文件。
*/ for (FileItem fileItem : parseRequest) {
if (fileItem.isFormField()) {//表示普通字段
if ("username".equals(fileItem.getFieldName())) {
String username = fileItem.getString();
writer.write("您的用户名:"+username+"<br>");
}
if ("userpass".equals(fileItem.getFieldName())) {
String userpass = fileItem.getString();
writer.write("您的密码:"+userpass+"<br>");
} }else {//表示是上传的文件
//不同浏览器上传的文件可能带有路径名,需要自己切割
String clientName = fileItem.getName();
String filename = "";
if (clientName.contains("\\")) {//如果包含"\"表示是一个带路径的名字,则截取最后的文件名
filename = clientName.substring(clientName.lastIndexOf("\\")).substring(1);
}else {
filename = clientName;
} UUID randomUUID = UUID.randomUUID();//生成一个128位长的全球唯一标识 filename = randomUUID.toString()+filename; /*
* 设计一个目录生成算法,如果所用用户上传的文件总数是亿数量级的或更多,放在同一个目录下回导致文件索引非常慢,
* 所以,设计一个目录结构来分散存放文件是非常有必要,且合理的
* 将UUID取哈希算法,散列到更小的范围,
* 将UUID的hashcode转换为一个8位的8进制字符串,
* 从这个字符串的第一位开始,每一个字符代表一级目录,这样就构建了一个八级目录,每一级目录中最多有16个子目录
* 这无论对于服务器还是操作系统都是非常高效的目录结构
*/
int hashUUID =randomUUID.hashCode();
String hexUUID = Integer.toHexString(hashUUID);
//System.out.println(hexUUID);
//获取将上传的文件存存储在哪个文件夹下的绝对路径
String filepath=request.getSession().getServletContext().getRealPath("upload");
for (char c : hexUUID.toCharArray()) {
filepath = filepath+"/"+c;
}
//如果目录不存在就生成八级目录
File filepathFile = new File(filepath);
if (!filepathFile.exists()) {
filepathFile.mkdirs();
}
//从Request输入流中读取文件,并写入到服务器
InputStream inputStream2 = fileItem.getInputStream();
//在服务器端创建文件
File file = new File(filepath+"/"+filename);
BufferedOutputStream bos = new BufferedOutputStream(new FileOutputStream(file)); byte[] buffer = new byte[10*1024];
int len = 0;
while ((len= inputStream2.read(buffer, 0, 10*1024))!=-1) {
bos.write(buffer, 0, len);
}
writer.write("您上传文件"+clientName+"成功<br>");
//关闭资源
bos.close();
inputStream2.close();
}
}
//注意Eclipse的上传的文件是保存在项目的运行目录,而不是workspace中的工程目录里。
二、文件上传需要特别注意的问题:(这些问题在上面的代码中都提供了简单的解决)
java.util.UUID,直接调用即可.
UUID uuid = UUID.randomUUID();
String s = UUID.randomUUID().toString();//用来生成数据库的主键id非常不错。。 UUID是由一个十六位的数字组成,表现出来的形式例如
550E8400-E29B-11D4-A716-446655440000
3、为防止单个目录下文件过多,影响文件读写速度,处理上传文件的程序应该应根据可能的上传总量,选择合适的目录结构生成算法,将上传文件分散存储。如使用hashcode方法构建多级目录。
<%
ArrayList<String> fileNames = new ArrayList<String>();
fileNames.add("file/aa.txt");
fileNames.add("file/bb.jpg");
for(String fileName : fileNames) {
%> <form action="DownloadServlet" method="get">
<input type="hidden" name="fileName" value="<%=fileName %>" />
<input type="submit" value="下载:<%=fileName %>" />
</form>
<%
}
%>
request.setCharacterEncoding("utf-8"); String filename = request.getParameter("fileName"); String urlname = URLEncoder.encode(filename, "utf-8");//防止文件名中有中文乱码
response.setHeader("Content-Disposition","attachment;filename="+urlname); FileInputStream fis = new FileInputStream(new File(request.getSession().getServletContext().getRealPath(filename)));
BufferedInputStream bis = new BufferedInputStream(fis);
ServletOutputStream sos = response.getOutputStream(); byte[] buffer = new byte[1024];
int len=0;
while((len=bis.read(buffer, 0, 1024))!=-1){
sos.write(buffer, 0, len);
}
bis.close();
fis.close();
JavaEE系列之(二)commons-fileupload实现文件上传、下载的更多相关文章
- Apache Commons fileUpload实现文件上传之一
需要两个jar包: commons-fileupload.jar Commons IO的jar包(本文使用commons-io-2.4.jar) 利用Servlet来实现文件上传. package ...
- Spring MVC使用commons fileupload实现文件上传功能
通过Maven建立Spring MVC项目,引入了Spring相关jar依赖. 1.为了使用commons fileupload组件,需要在pom.xml中添加依赖: <properties&g ...
- Apache Commons FileUpload 实现文件上传
Commons FileUpload简介 Apache Commons是一个专注于可重用Java组件开发的 Apache 项目.Apache Commons项目由三个部分组成: 1.Commons P ...
- Apache Commons FileUpload实现文件上传
一.Apache Commons-FileUpload简介 Apache Commons是一个专注于可重用Java组件的所有方面的 Apache 项目. Apache Commons项目由三个部分组成 ...
- 使用fileupload实现文件上传
一. fileupload组件工作原理 先来张图片, 帮助大家理解 fileupload核心API 1. DiskFileItemFactory构造器1) DiskFileItemFactory() ...
- JSP 文件上传下载系列之二[Commons fileUpload]
前言 关于JSP 文件上传的基础和原理在系列一中有介绍到. 这里介绍一个很流行的组件commons fileupload,用来加速文件上传的开发. 官方的介绍是: 让添加强壮,高性能的文件到你的se ...
- javaEE(14)_文件上传下载
一.文件上传概述 1.实现web开发中的文件上传功能,需完成如下二步操作: •在web页面中添加上传输入项•在servlet中读取上传文件的数据,并保存到本地硬盘中. 2.如何在web页面中添加上传输 ...
- 使用Typescript重构axios(二十五)——文件上传下载进度监控
0. 系列文章 1.使用Typescript重构axios(一)--写在最前面 2.使用Typescript重构axios(二)--项目起手,跑通流程 3.使用Typescript重构axios(三) ...
- salesforce 零基础学习(四十二)简单文件上传下载
项目中,常常需要用到文件的上传和下载,上传和下载功能实际上是对Document对象进行insert和查询操作.本篇演示简单的文件上传和下载,理论上文件上传后应该将ID作为操作表的字段存储,这里只演示文 ...
随机推荐
- python中的多线程【转】
转载自: http://c4fun.cn/blog/2014/05/06/python-threading/ python中关于多线程的操作可以使用thread和threading模块来实现,其中th ...
- 15+ 易响应的CSS框架快速开启你的敏捷网站项目
由 于移动互联用户的快速增加,现在数量已经超出10亿,几乎可以肯定的是你的网站每天都会有移动用户访问.如果你组织计划创建一个对移动用户友好的浏览体 验,有多个方面需要考虑.响应快速的网站设计似乎现在很 ...
- Python线程
原文出处: AstralWind 1. 线程基础 1.1. 线程状态 线程有5种状态,状态转换的过程如下图所示: 1.2. 线程同步(锁) 多线程的优势在于可以同时运行多个任务(至少感觉起来是这样). ...
- 基于MapReduce的关系代数运算(2)
1.自然连接 Map函数:对于R中的每个元组(a,b),生成键值对(b,(R,a)),对于S中的每个元组(b,c),生成键值对(b,(S,c)) Reduce函数:每个键值b会与一系列对相关联,这些对 ...
- Android 横屏时禁止输入法全屏
在自己EditText的xml里加上属性 android:imeOptions="flagNoExtractUi"
- js获取时间搓
var oData=new Date().getTime(2016-01-16); console.log(oData);
- 【MySql】在Linux下安装MySql数据库
[参数环境] 1.Host OS:Win7 64bit 2.VM: VMware 11.1.0 3.Client OS:CentOS 6 4.系统中已安装的openssl版本: openssl-1.0 ...
- MacTerminal快捷键
[MacTerminal快捷键] 在Mac系统中并没有Home.End等键,所以在使用时并不是特别的顺手,但是有几个键位组合可以使Terminal的操作更加灵活方便. 1.将光标移动到行首:ctrl ...
- 微软IOC容器Unity简单代码示例2-配置文件方式
@(编程) 1. 通过Nuget下载Unity 这个就不介绍了 2. 接口代码 namespace UnityDemo { interface ILogIn { void Login(); } } n ...
- 做 fzu oj 1106 题目学到的
题目如下 这道题的意识就是给一个数问是否可以又阶乘之和构成,而难点主要是在于如果是7的话就是1!+3!,并不是单纯的从1的阶乘开始加,而是没顺序的,所以这题就得用到递归. (大概就是函数自己调用函数自 ...