26.文件上传与下载
基于表单的文件上传
? 如果在表单中使用表单元素<input type=“file” />,浏览器在解析表单时,会自动生成一个输入框和一个按钮,输入框可供用户填写本地文件的文件名和路径名,按钮可以让浏览器打开一个文件选择框供用户选择文件:
Enctype 属性
- 当表单需要上传文件时,需指定表单 enctype 的值为 multipart/form-data;
- 在 form 元素的语法中,enctype 属性指定将数据发送到服务器时浏览器使用的编码类型。
- enctype 属性取值:
- –application/x-www-form-urlencoded:表单 enctype 属性的默认值,这种编码方案使用有限的字符集。
- –multipart/form-data:form 设定了enctype=“multipart/form-data”属性后,表示表单以二进制传输数据。
Commons-fileupload 组件
- Commons-fileupload 组件是 Apache 开源代码组织用来处理表单文件上传的一个子项目,该组件性能优异,可以支持任意大小的文件的上传
- Commons-fileupload 组件从 1.1 版本开始依赖 Apache 的另一个项目:commons-io
Commons-fileupload 组件上传的基本原理
- FileUpload组件将页面提交的所有元素(普通form表单域,如text和文件域file)都看作一样的FileItem,这样上传页面提交的 request请求也就是一个FileItem的有序组合;
- FileUpload组件可以解析该request,并返回一个一个的FileItem;
- 对每一个FileItem,FileUpload组件可以判断出它是普通form表单域还是文件file域,从而根据不同的类型,采取不同的操作;
- 如果是表单域,就读出其值,如果是文件域,就保存文件到服务器硬盘上或者内存中。
Commons-fileupload 组件API
-
在 Commons-fileupload 组件中,主要用到以下三个接口和类:
-
ServletFileUpload 负责处理上传的文件数据,并将每部分的数据封装成一到 FileItem 对象中。
-
DiskFileItemFactory 是创建 FileItem 对象的工厂,在这个工厂类中可以配置内存缓冲区大小和存放临时文件的目录。
-
ServletFileUpload 在接收上传文件数据时,会将内容保存到内存缓存区中,如果文件内容超过了 DiskFileItemFactory 指定的缓冲区的大小,那么文件将被保存到磁盘上,存储为 DiskFileItemFactory 指定目录中的临时文件。等文件数据都接收完毕后,ServletUpload 在从文件中将数据写入到上传文件目录下的文件中。
核心API—DiskFileItemFactory
DiskFileItemFactory 是创建FileItem 对象的工厂,这个工厂类常用方法:
- 1、public void setSizeThreshold(int sizeThreshold) :设置内存缓冲区的大小,默认值为10K。当上传文件大于缓冲区大小时,fileupload组件将使用临时文件缓存上传文件。
- 2、public void setRepository(Java.io.File repository) :指定临时文件目录,默认值为System.getProperty(“java.io.tmpdir”).
- 3、public DiskFileItemFactory(int sizeThreshold,java.io.File repository) :构造函数
核心API—-ServletFileUpload
ServletFileUpload 负责处理上传的文件数据,并将表单中每个输入项封装成一个FileItem 对象中。常用方法有:
- 1、boolean isMultipartContent(HttpServletRequest request) :判断上传表单是否为multipart/form-data类型
- 2、List parseRequest(HttpServletRequest request):解析request对象,并把表单中的每一个输入项包装成一个fileItem 对象,并返回一个保存了所有FileItem的list集合。
- 3、setFileSizeMax(long fileSizeMax) :设置上传文件的最大值(单个文件),用于设置单个上传文件的最大尺寸限制,以防止客户端恶意上传超大文件来浪费服务器端的存储空间。其参数是以字节为单位的long型数字。
- 4、setSizeMax(long sizeMax) :设置上传文件总量的最大值(所有上传文件),用于设置请求消息实体内容(即所有上传数据)的最大尺寸限制,以防止客户端恶意上传超大文件来浪费服务器端的存储空间。其参数是以字节为单位的long型数字。
- 5、setHeaderEncoding(java.lang.String encoding) :设置编码格式。在文件上传请求的消息体中,除了普通表单域的值是文本内容以外,文件上传字段中的文件路径名也是文本,在内存中保存的是它们的某种字符集编码的字节数组,Apache文件上传组件在读取这些内容时,必须知道它们所采用的字符集编码,才能将它们转换成正确的字符文本返回。
核心API—FileItem
- 1、boolean isFormField(): isFormField方法用于判断FileItem类对象封装的数据是一个普通文本表单字段,还是一个文件表单字段,如果是普通表单字段则返回true,否则返回false。
- 2、 String getName()用于获得文件上传字段中的文件名。注意IE或FireFox中获取的文件名是不一样的,IE中是绝对路径,FireFox中只是文件名。
- 3、String getFieldName()用于返回表单标签name属性的值。
- 4、 void write(File file):用于将FileItem对象中保存的主体内容保存到某个指定的文件中。如果FileItem对象中的主体内容是保存在某个临时文件中,该方法顺利完成后,临时文件有可能会被清除。该方法也可将普通表单字段内容写入到一个文件中,但它主要用途是将上传的文件内容保存在本地文件系统中。
- 5、 String getString():用于将FileItem对象中保存的数据流内容以一个字符串返回,它有两个重载的定义形式:
public Java.lang.String getString();
public java.lang.String getString(java.lang.String encoding) throws java.io.UnsupportedEncodingException
前者使用缺省的字符集编码将主体内容转换成字符串,后者使用参数指定的字符集编码将主体内容转换成字符串。如果在读取普通表单字段元素的内容时出现了中文乱码现象,请调用第二个getString方法,并为之传递正确的字符集编码名称。 - 6、 void delete():delete方法用来清空FileItem类对象中存放的主体内容,如果主体内容被保存在临时文件中,delete方法将删除该临时文件。尽管当FileItem对象被垃圾收集器收集时会自动清除临时文件,但及时调用delete方法可以更早的清除临时文件,释放系统存储资源。
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
//1、创建DiskFileItemFactory对象,设置缓冲区大小和临时文件目录。
DiskFileItemFactory factory = new DiskFileItemFactory();
factory.setSizeThreshold(1024 * 500);
File tempDirectory = new File("d:\\tempDirectory");
factory.setRepository(tempDirectory);
//2、使用DiskFileItemFactory 对象创建ServletFileUpload对象,并设置上传文件的大小限制。
ServletFileUpload upload = new ServletFileUpload(factory);
upload.setSizeMax(1024 * 1024 * 5);
try {
//3、调用ServletFileUpload.parseRequest方法解析request对象,得到一个保存了所有上传内容的List对象。
List<FileItem> items = upload.parseRequest(req);
//4、对list进行迭代,每迭代一个FileItem对象,调用其isFormField方法判断是否是上传文件:
for (FileItem item : items) {
//4.1、 为普通表单字段,则调用getFieldName、getString方法得到字段名和字段值。
if (item.isFormField()) {
String name = item.getFieldName();
String value = item.getString();
System.out.println(name + ": " + value);
}
//4.2、为上传文件,则调用getInputStream方法得到数据输入流,从而读取上传数据。
else {
String fieldName = item.getFieldName();
String fileName = item.getName();
String contentType = item.getContentType();
long sizeInBytes = item.getSize();
System.out.println(fieldName);
System.out.println(fileName);
System.out.println(contentType);
System.out.println(sizeInBytes);
InputStream in = item.getInputStream();
byte[] buffer = new byte[1024];
int len = 0;
File file = new File("d://files");
if (!file.exists()){
file.mkdir();
}
fileName = "d:\\files\\" + fileName;
System.out.println(fileName);
OutputStream out = new FileOutputStream(fileName);
while ((len = in.read(buffer)) != -1) {
out.write(buffer, 0, len);
}
out.close();
in.close();
}
}
} catch (FileUploadException e) {
e.printStackTrace();
}
}
文件的下载
- 下载文件的关键是几点:
- 服务器以一个流的形式将文件发送给浏览器。
- 发送流的同时还需要设置几个响应头,来告诉浏览器下载的信息。
- 具体响应头如下:
- Content-Type
- 下载文件的MIME类型
- 可以通过 getMimeType(String file)获取
- 也可以直接手动指定
- 使用setContentType(String type);
- 响应头样式:
- Content-Type: audio/mpeg
- Content-Disposition
- 下载文件的名字,主要作用是提供一个默认的用户名
- 通过setHeader(“Content-Disposition”, disposition)设置
- 响应头样式:
- Content-Disposition: attachment; filename=xxx.mp3
- Content-Length
- 下载文件的长度,用于设置文件的长处(不必须)
- 通过 setContentLength(int len)设置。
- 设置后样式:
- Content-Length: 3140995
- Content-Type
- 接下来需要以输入流的形式读入硬盘上的文件
- FileInputStream is = new FileInputStream(file);
- 这个流就是我们一会要发送给浏览器的内容
- 通过response获取一个输出流,并将文件(输入流)通过该流发送给浏览器
- 获取输出流
- ServletOutputStream out = response.getOutputStream();通过输出流向浏览器发送文件(不要忘了关闭输入流)
- 获取输出流
byte[] b = new byte[1024];
int len = 0;
while((len=is.read(b))> 0){
out.write(b, 0, len);
}
is.close();
步骤:
- 获取文件的流:
String realPath = getServletContext().getRealPath(“/WEB-INF/mp3/中国话.mp3”);
//获取文件的File对象
File file = new File(realPath);
//获取文件的输入流
FileInputStream is = new FileInputStream(file); - 获取头信息
//获取文件的MIME信息
String contentType = getServletContext().getMimeType(realPath);
//设置下载文件的名字
String filename = “zhongguohua.mp3”;
//创建Content-Disposition信息
String disposition = “attachment; filename=”+ filename ;
//获取文件长度
long size = file.length() - 设置头信息
//设置Content-Type
response.setContentType(contentType);
//设置Content-Disposition
response.setHeader(“Content-Disposition”, disposition);
//设置文件长度
response.setContentLength((int)size) - 发送文件
//通过response获取输出流,用于向浏览器输出内容
ServletOutputStream out = response.getOutputStream();
//将文件输入流通过输出流输出
byte[] b = new byte[1024];
int len = 0;
while((len=is.read(b))> 0){
out.write(b, 0, len);
}
//最后不要忘记关闭输入流,输出流由Tomcat自己处理,我们不用手动关闭
is.close()
乱码:
至此实际上文件下载的主要功能都已经完成。但是还有一个问题我们这里没有体现出来,因为目前我们的文件名使用的是纯英文的,没有乱码问题。这里如果我们要使用中文文件名的话,毫无疑问会出现乱码问题。
解决此问题的方法很简单,在获取文件名之后为文件名进行编码:
fileName = java.net.URLEncoder.encode(filename,"utf-8");
或者先用GBK解码,再使用iso8859-1编码
fileName = new String(name.getBytes("gbk"),"iso8859-1");
但是注意这里火狐浏览器比较特殊,因为他默认是以BASE64解码的,所以这块如果需要考虑火狐的问题的话还需要特殊处理一下。
先要获取客户端信息(通过获取请求头中的User-Agent信息)
//获取客户端信息
String ua = request.getHeader("User-Agent")
然后判断浏览器版本,做不同的处理(通过判断头信息中是否包含Firefox字符串来判断浏览器版本)
//判断客户端是否为火狐
if(ua.contains("Firefox")){
//若为火狐使用BASE64编码
filename = "=?utf-8?B?"+new BASE64Encoder()
.encode(filename.getBytes("utf-8"))+"?=";
}else{
//否则使用UTF-8
filename = URLEncoder.encode(filename,"utf-8");
本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。 如若内容造成侵权/违法违规/事实不符,请联系我的编程经验分享网邮箱:veading@qq.com进行投诉反馈,一经查实,立即删除!