文件上传
一、文件上传入门
1. 应用:
用户上传头像、上传图片、邮件上传附件等
2. 页面表单的实现
文件上传表单和普通表单有两个区别
1) 需要文件上传字段 <input type=”file” />
2) form 表单的 enctype 属性需要指定为 multipart/form-data
3) 文件的提交方式必须为POST
<form action="${pageContext.request.contextPath}/servlet/UploadServlet3" method="post" enctype="multipart/form-data">
用户名:<input type="text" name="username"/><br/>
文件1:<input type="file" name="file1"/><br/>
文件2:<input type="file" name="file2"/><br/>
<input type="submit" value="上传"/>
3. 服务器端解析request
在 Servlet 中通过request.getInputStream 获得表单上传数据,会发现数据是分段发送的
使用Apache 开发的开源组件Commons-fileupload需要导入 jar 包Commons-fileupload 和Commons-io
3 . UploadServlet 中处理文件上传程序
所需类:1 工厂类:DiskFileItemFactory
2 负责处理上传的文件数据,并将表单中每个输入项封装成一个 FileItem 对象中:ServletFileUpload
3 代表字段的类: FileItem
// 1. 创建工厂类
DiskFileItemFactory factory = new DiskFileItemFactory();
// 2. 创建FileUpload对象
ServletFileUpload upload = new ServletFileUpload(factory);
// 3. 判断是否是上传表单
boolean b = upload.isMultipartContent(request);
if(!b) {
// 不是文件上传
request.setAttribute("message", "对不起,不是文件上传表单!");
request.getRequestDispatcher("/message.jsp").forward(request, response);
return;
}
// 是文件上传表单
// 4. 解析request,获得FileItem项
List<FileItem> fileitems = upload.parseRequest(request);
// 5. 遍历集合
for(FileItem item : fileitems) {
// 判断是不是普通字段
if(item.isFormField()) {
String name = item.getFieldName();
String value = item.getString();
// 手工的转换了
value = new String(value.getBytes("iso-8859-1"),"utf-8");
System.out.println(name + "=" + value);
}
else {
// 文件上传字段
// 获得文件名
String filename = item.getName();
System.out.println(filename);
filename = filename.substring(filename.lastIndexOf("\\")+1);
System.out.println(filename);
// 创建文件
ServletContext context = getServletContext();
String dir = context.getRealPath("WEN-INF/upload");
File file = new File(dir, filename);
file.createNewFile();
// 获得流,读取数据写入文件
InputStream in = item.getInputStream();
FileOutputStream fos = new FileOutputStream(file);
int len;
byte[] buffer = new byte[1024];
while((len=in.read(buffer))>0)
fos.write(buffer,0,len);
fos.close();
in.close();
item.delete(); //删除临时文件
}
二、 文件上传处理细节
1. 中文乱码问题
1) 文件名中文乱码问题,解决办法:告诉文件上传组件以什么编码方式来解码文件名
ServletUpload.setCharacterEncoding(“utf-8”);
request. setCharacterEncoding(“utf-8”);
2) 普通字段中文乱码问题
fileitem.getString(“utf-8”);
2. 临时文件
对于大文件不能缓存在内存,需要缓存到硬盘,为了方便管理,我们需要设置临时文件存放目录
• public void setSizeThreshold(int sizeThreshold)
• 设置内存缓冲区的大小,默认值为10K。当上传文件大于缓冲区大小时, fileupload组件将使用临时文件缓存上传文件。
• public void setRepository(java.io.File repository)
• 指定临时文件目录,默认值为System.getProperty("java.io.tmpdir").
文件上传完毕需要删除临时文件,否则会导致服务器存在两份上传文件
// 注意,需要先将流进行关闭,否则会导致临时文件无法删除
out.close();
in.close();
// 删除临时文件
fileitem.delete();
3. 文件存放目录
1) 目录需要隐藏,禁止外界直接访问
2) 文件名需要保证不重复
3) 文件应该分目录存放
4、多次上传同名文件的覆盖
上传的文件如果名称不同就不会覆盖。3298432984_a.txt
UUID_源文件名
5、如何防止同一目录下文件太多的问题
打撒后存储
利用文件名的hash码打散存储
6、上传文件的大小控制(单个文件和总大小),及如何友好提示用户
单个文件:
ServletFileUpload setFileSizeMax(long)字节
抓异常:FileUploadBase.FileSizeLimitExceededException
总大小:
ServletFileUpload setSizeMax
抓异常:FileUploadBase.SizeLimitExceededException
7、限制上传文件的类型
不能严格的控制。可通过判断上传文件的扩展名。
8、监听文件的上传进度
注册监听器
upload.setProgressListener(new ProgressListener() {
//pBytesRead已上传数据的大小
//pContentLength文件总大小
//pItems上传的第几个
public void update(long pBytesRead, long pContentLength, int pItems) {
System.out.println("已上传数据的大小"+pBytesRead+",总大小"+pContentLength);
}
});
9、用户没有选择文件上传时的问题
if(item==null)
continue; //防止用户没有上传文件时,点击上传出现的异常情况