FileUpload是apache commons中的一个组件,FileUpload依赖apache io包,最新的最新的FileUpload可以很好的解析以MIME协议(form表单设置为enctype="multipart/form-data")上传的文件,并对文件进行读写操作,而不用关心如缓存等问题。
以下是文件上传的web层代码:
package com.hao.web.controller;
import com.hao.domain.Upfile;
import com.hao.factory.Factory;
import com.hao.service.BusinessService;
import org.apache.commons.beanutils.BeanUtils;
import org.apache.commons.fileupload.FileItemIterator;
import org.apache.commons.fileupload.FileItemStream;
import org.apache.commons.fileupload.FileUploadBase;
import org.apache.commons.fileupload.ProgressListener;
import org.apache.commons.fileupload.disk.DiskFileItemFactory;
import org.apache.commons.fileupload.servlet.ServletFileUpload;
import org.apache.commons.fileupload.util.Streams;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.util.Date;
import java.util.UUID;
public class UploadServlet extends HttpServlet {
@Override
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
request.getRequestDispatcher(request.getContextPath() + "/WEB-INF/jsp/upload.jsp").forward(request, response);
}
@Override
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
DiskFileItemFactory itemFactory = new DiskFileItemFactory();
//设置内存缓冲区的大小,默认值为10K。当上传文件大于缓冲区大小时, fileupload组件将使用临时文件缓存上传文件
itemFactory.setSizeThreshold(1024*1024);
//设置缓存位置,默认值为System.getProperty("java.io.tmpdir"):这里设为应用目录下的/temp目录
File repository = new File(request.getServletContext().getRealPath("/temp"));
itemFactory.setRepository(repository);
//如果CleaningTracker不为空,临时文件会自动删除
//itemFactory.setFileCleaningTracker(null);
//CleaningTracker应该在不使用时应该被清除,只要在web.xml中配置
// <listener>
// <listener-class>
// org.apache.commons.fileupload.servlet.FileCleanerCleanup
// </listener-class>
// </listener>
ServletFileUpload fileUpload = new ServletFileUpload(itemFactory);
//设置上传的文件名编码(注意,普通字段编码要单独设置)
fileUpload.setHeaderEncoding("UTF-8");
//设置单个文件最大字节数,单个文件大于这个值解析时抛出FileUploadBase.FileSizeLimitExceededException
fileUpload.setFileSizeMax(524288000);
//设置一个完整的请求的最大允许大小,请求大于这个值解析时抛出FileUploadBase.SizeLimitExceededException
fileUpload.setSizeMax(534288000);
//注册监听器:可以用于做进度条,这里使每上传1M打印一次进度
fileUpload.setProgressListener(new ProgressListener() {
private long init = -1;
@Override
public void update(long pBytesRead, long pContentLength, int pItems) {
long ar = pBytesRead / 1048576;
if (ar == init) {
return;
}
init = ar;
//注意,表单元素也会使pItems自增
System.out.print("正在上传" + pItems);
if (pContentLength == -1) {
System.out.println("已经上传了" + ar + "Mb");
} else {
System.out.println("已经上传了" + ar + "Mb/" + pContentLength / 1048576f + "Mb");
}
}
});
//判断封装在request中的上传表单是否为multipart/form-data类型
t:if (fileUpload.isMultipartContent(request)) {
Upfile upfile = new Upfile();
upfile.setUptime(new Date());
try {
FileItemIterator itemIterator = fileUpload.getItemIterator(request);
while (itemIterator.hasNext()) {
FileItemStream item = itemIterator.next();
//得到字段名
String name = item.getFieldName();
//得到字段对应的流
InputStream stream = item.openStream();
//如果是普通字段输入项:将输入项的值保存
if (item.isFormField()) {
//记得设置编码,防止乱码
String value = Streams.asString(stream,"UTF-8");
BeanUtils.setProperty(upfile, name, value);
} else {
String filename = item.getName();
filename = filename.substring(filename.lastIndexOf("\\") + 1);
if (filename.trim().equals("")){
request.setAttribute("message","上次文件不能为空");
break t;
}
BeanUtils.setProperty(upfile, "filename", filename);
saveFile(stream, upfile, request.getServletContext().getRealPath("/WEB-INF/upload"));
}
}
BusinessService bs = Factory.getInstance().create(BusinessService.class);
bs.addFile(upfile);
request.setAttribute("message", "上传成功");
} catch (FileUploadBase.SizeLimitExceededException | FileUploadBase.FileSizeLimitExceededException e) {
request.setAttribute("message", "上传文件不能超过5Mb");
} catch (Exception e) {
e.printStackTrace();
request.setAttribute("message", "未知错误");
}
} else {
request.setAttribute("message", "上传文件失败");
}
request.getRequestDispatcher("/WEB-INF/jsp/message.jsp").forward(request, response);
}
private void saveFile(InputStream in, Upfile upfile, String rootPath) throws IOException {
FileOutputStream out = null;
try {
String filename = upfile.getFilename();
//由文件名的文件路径,以分包储存,提高文件目录性能
String id = UUID.randomUUID().toString();
int i = id.hashCode();
int dir1 = i & 15;
int dir2 = i >>> 4 & 15;
upfile.setId(id);
File savedir = new File(rootPath + File.separator + dir1 + File.separator + dir2 );
savedir.mkdirs();
File savepath = new File(rootPath + File.separator + dir1 + File.separator + dir2 + File.separator + id + "_" + filename);
upfile.setSavepath(savepath.getAbsolutePath());
byte[] buff = new byte[1024];
int len;
out = new FileOutputStream(savepath);
while ((len = in.read(buff)) > 0) {
out.write(buff, 0, len);
}
} finally {
in.close();
if (out != null) {
out.close();
}
}
}
}
ThreadLocal可以将保存在其中的数据与线程号绑定,实现同一线程的数据方便访问。使用ThreadLocal将查询信息从web层存储,在dao层就可以取出进行查询,相比于使用参数传递来传递查询信息,该方法可以方便实现跨层传输信息,使用起来非常清爽,代码也简单明了。实现如下:
用于封装查询信息的FileQueryInfo类:
package com.hao.domain;
//由web层创建封装用户查询信息,给dao层使用,要跨层传递数据,可以用ThreadLocal容器
public class FileQueryInfo {
private int currentpage = 1;
private int pagesize = 5;
private int currentrecord;
private static ThreadLocal<FileQueryInfo> tl = new ThreadLocal();
public FileQueryInfo() {
tl.set(this);
}
public static FileQueryInfo getInfo() {
return tl.get();
}
public static void removeInfo() {
tl.remove();
}
public int getCurrentpage() {
return currentpage;
}
public void setCurrentpage(int currentpage) {
if (currentpage < 1) {
this.currentpage = 1;
} else {
this.currentpage = currentpage;
}
}
public int getPagesize() {
return pagesize;
}
public void setPagesize(int pagesize) {
if (pagesize < 1) {
this.pagesize = 5;
} else {
this.pagesize = pagesize;
}
}
public int getCurrentrecord() {
currentrecord = (currentpage - 1) * pagesize;
return currentrecord;
}
}
用于返回查询信息,并在jsp被调用输出的FileQueryBean类:
package com.hao.domain;
import java.util.List;
//由service层创建,封装web层要用的数据
public class FileQueryBean {
//数据库传的数据 : get set 不用多管
private int totalrecord;
private List<Upfile> records;
//用户传的数据
private int currentpage;
private int pagesize;
//自动生成的数据 : 无set只能get,也可以不设计为字段
private int nextpage;
private int previouspage;
private int totalpage;
private String[] pagebar;
public int getNextpage() {
nextpage = currentpage + 1;
if (nextpage > getTotalpage()) {
nextpage = getTotalpage();
}
return nextpage;
}
public int getPreviouspage() {
if (currentpage <= 1) {
previouspage = 1;
} else {
previouspage = currentpage - 1;
}
return previouspage;
}
public int getTotalpage() {
totalpage = (totalrecord - 1) / pagesize + 1;
return totalpage;
}
public String[] getPagebar() {
int start;
int end;
//这个bar长为10
if (getTotalpage() < 10) {
start = 1;
end = getTotalpage();
} else {
if (currentpage > getTotalpage() - 5) {
end = getTotalpage();
start = getTotalpage() - 9;
} else {
start = currentpage - 4;
end = currentpage + 5;
}
}
pagebar = new String[end - start + 1];
for (int i = 0; i < pagebar.length; i++) {
pagebar[i] = start + i + "";
}
return pagebar;
}
public int getCurrentpage() {
return currentpage;
}
public void setCurrentpage(int currentpage) {
this.currentpage = currentpage;
}
public int getTotalrecord() {
return totalrecord;
}
public void setTotalrecord(int totalrecord) {
this.totalrecord = totalrecord;
}
public int getPagesize() {
return pagesize;
}
public void setPagesize(int pagesize) {
this.pagesize = pagesize;
}
public List<Upfile> getRecords() {
return records;
}
public void setRecords(List<Upfile> records) {
this.records = records;
}
}
控制最后数据输出的Servlet与JSP:
@Override
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
try {
BusinessService bs = Factory.getInstance().create(BusinessService.class);
FileQueryInfo queryInfo = new FileQueryInfo();
BeanUtils.populate(queryInfo, request.getParameterMap());
FileQueryBean filePage = bs.getFilePage();
request.setAttribute("result", filePage);
request.getRequestDispatcher("/WEB-INF/jsp/download.jsp").forward(request, response);
} catch (Exception e) {
request.setAttribute("message", "未知错误");
request.getRequestDispatcher("/WEB-INF/jsp/message.jsp").forward(request, response);
}
}
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
<html>
<head>
<title>下载视图</title>
</head>
<body>
<div align="center">
<table border="1" width="80%">
<tr>
<td>
文件名
</td>
<td>
上传用户
</td>
<td>
上传时间
</td>
<td>
文件描述
</td>
<td>
操作
</td>
</tr>
<c:forEach var="record" items="${result.records }">
<tr>
<td>
${record.filename }
</td>
<td>
${record.username }
</td>
<td>
${record.uptime }
</td>
<td>
${record.description }
</td>
<td>
<a href="DownloadServlet?id=${record.id }" methods="post">下载</a>
<a href="ModifyServlet?id=${record.id }">修改</a>
<a href="DeleteServlet?id=${record.id }">删除</a>
</td>
</tr>
</c:forEach>
</table>
</div>
<div align="center">
共${result.totalrecord }条记录,每页${result.pagesize }条记录,共${result.totalpage }页,当前第${result.currentpage }页
<a href="?currentpage=${result.previouspage }">上一页</a>
<c:forEach var="bar" items="${result.pagebar }">
<c:choose>
<c:when test="${bar==result.currentpage }">
${bar }
</c:when>
<c:otherwise>
<a href="?currentpage=${bar }">${bar }</a>
</c:otherwise>
</c:choose>
</c:forEach>
<a href="?currentpage=${result.nextpage }">下一页</a>
</div>
</body>
</html>
最会基本效果是: