文件上传与下载

一、文件上传表单(common-fileupload-xxx.jar包和common-io-xxx.jar包)
一】分析:
1)上传文件的本质是一个文本复制的过程
2)技术层面,在Java中一定会用到IO操作,主要以字节流的方式
3)传统方式下,对于上传文件字段不同的浏览器有着不同的解析方式。(例如:IE6:D:\a.jpg 、非IE6:a.jpg)
4)可以将form以MIME协议的方式将文件上传到服务端,服务端以二进制流的方式读写.

二】web端:在web页面中添加上传输入项 ----使用<input type="file">标签
1)必须要设置name属性,否则浏览器将不会发送上传文件数据
2)必须把form的enctype属性值设置为multipart/form-data.
(此时会将文件数据分块后、附带在http请求的消息体中,并使用MIMB协议对上传的文件进行描述,一边接收方对上传的数据进行解析和处理。)
3)(需使用common-fileupload-xxx.jar包和common-io-xxx.jar包)
三】service端:
步骤:
1)创建一个上传文件工厂
DiskFileItemFactory factory = new DiskFileItemFactory();
2)设置内存中缓存区的大小
factory.setSizeThreshold(100*1024);//设置内存缓冲区大小为100K
3)设置上传文件使用的临时目录
factory.setRepository(new File(tempPath));
4)创建上传文件对象【核心对象】
ServletFileUpload upload = new ServletFileUpload(factory);
5)判断文件是否以MIME协议上传的,如果没有使用,则抛出异常

Boolean flag = upload.isMultipartContent(request);
if (!flag)
throw new FileUploadException();

  


API:
1)DiskFileItemFactory常用方法:
a)构造函数
public DiskFileItemFactory(int sizeThreshold, File repository);
b)设置内存缓冲区的大小:默认为10K
public void setSizeThreshold(int sizeThreshold);
c)设置临时文件目录:默认为java.io.tmpdir
public void setRepository(java.io.File repository);
2)ServletFileUpload类:
a)判断上传文件是否使用MIME协议.(加了multipart/form-data的表单)
public boolean isMultipartContent(request);//是,返回true;否,返回false.
b)解析出request中所有的文件形成一个List集合:FileItem表示上传表单中的表单项内容.
List<FileItem> parseRequest(HttpServletRequest request);
c)设置上传的文件名称的编码
setHeaderEncoding(String encoding);//encoding设置为UTF-8可以解决上传文件的名称的中文乱码问题
3)FileItem类(代表普通字段和上传字段两类)
a)是否为表单的普通字段:是,true;否,false.
boolean isFormFiled();
b)得到普通字段元素的属性名
String getFiledName();
c)得到普通字段元素的值
String getString(String endcoding); //encoding设置为UTF-8可以解决普通字段的中文乱码问题
d)得到上传文件的名称
String getName();

二、上传文件详情
一】中文乱码的问题
1)普通字段的中文乱码问题:
fileItem.getString("UTF-8");即可.
2)上传字段的中文乱码问题:
void setHeaderEncoding("UTF-8");
二】临时文件的删除问题
a)通过 FileItem类的delete()方法即可删除
b)但删除必须要在关闭IO流之后,否则删除不成功
三】在同一个目录下上传相同文件名的问题:
a)只需要在文件名的前面拼接一个UUID码作为唯一的标识符即可
四】单个目录下文件过多的问题
可以通过算法解决,代码如下:
code:

private static DBUploadFile initDBUploadFile(FileItem item,HttpServletRequest request, String savePath) { 
String realName = getRealName(item);
String uuidName = getUUIDName(realName);
// 得到实际存储目录
String upLoadPath = request.getServletContext().getRealPath(
"\\WEB-INF\\" + savePath);
// 创建文件目录,防止一个文件夹下面的文件过多
String dirsPath = "\\";
Integer fileCode = realName.hashCode();
Integer tempCode = fileCode;
while (0 != (tempCode = fileCode & 0XF)) {
dirsPath += (tempCode + "\\");
fileCode >>>= 4;
}

// 拼接字符串形成完整文件存储路径
String upLoadFilePath = upLoadPath + dirsPath + uuidName;
File upLoadFile = new File(upLoadFilePath);
// 创建文件夹,防止抛出FileNotFind异常
File dirFile = upLoadFile.getParentFile();
if (!dirFile.exists()) {
dirFile.mkdirs();
}
DBUploadFile dbUploadFile = new DBUploadFile();
dbUploadFile.setFileName(realName);
dbUploadFile.setFilePath(upLoadFilePath);
dbUploadFile.setFileItem(item);
return dbUploadFile;
}

 

 

 

五】为安全将上传的文件放入客户端无法直接访问的目录中的问题
a)将上传的文件,放置到/WEB-INF/upload/目录下
六】上传文件大小和文件类型的问题:
通过判断后抛出自定义异常的方式
code:

// 如果未上传文件
long fileSize = item.getSize();
long maxSize = 200 * 1024;
if (0 == fileSize)
throw new NoUploadFileException();
if (maxSize < fileSize) {
item.delete();
throw new UploadFileSizeException(fileSize, maxSize);
}

 

 

 

七】上传多个文件的界面问题:
三】显示下载文件列表(硬盘版)
a)递归方式查询可供下载的文件,一定要有出口条件
b)使用Map<UUID文件名,真实文件名>收集可供下载的文件
c)使用<c:url>和<c:param>对中文名进行URL编码

四】下载文件
a)对传过来的中文编码进行URL解码
b)通过UUID文件名,反向查到该文件所在的真实目录

五】 文件上传下载与数据库结合
a)在将上传文件保存的同时,写往数据库表,一个上传文件对应一条记录,确保uuidFileName双方一致

思考:
a)上传时,先硬盘,再表?
b)下载时,先硬盘,再表?
c)删除时,先硬盘,再表?
d)是否需要事务支持?

1 文件上传下载和数据库结合
1)速度:较小文件的文件存入数据库中,取出速度较快,返之较慢。
较大文件的存入硬盘中,取出速度相对于数据库较快。
2)同步:
数据库表和硬盘必须一致,必须要事务的支持
在事务的情况下,表操作优先,硬盘其后
结论:
1)上传时,先数据库,后硬盘
2)删除时,先数据库,后硬盘
原因:数据库异常,可以进行回滚,硬盘内容不能进行回滚

 

 

posted on 2014-12-24 11:36  SkyGood  阅读(366)  评论(0编辑  收藏  举报