目录
一、文件上传
文件上传的细节
文件上传功能有许多需要注意的小细节问题,以下列出的几点需要特别注意的
1、为保证服务器安全,上传文件应该放在外界无法直接访问的目录下,比如放于WEB-INF目录下。
2、为防止文件覆盖的现象发生,要为上传文件产生一个唯一的文件名。
3、为防止一个目录下面出现太多文件,要使用hash算法打散存储。
4、要限制上传文件的最大值。
5、要限制上传文件的类型,在收到上传文件名时,判断后缀名是否合法。
1.1 JSP页面
当客户端向服务器上传文件时,客户端发送的HTTP请求正文采用"multipart/form-data"数据类型,它表示复杂的包括多个子部分的复合表单。例如下面的代码所示
<form action="UploadDemo" method="post" enctype="multipart/form-data">
姓名:<input type="text" name="name"/><br/>
性别:<input type="text" name="sex"/><br/>
<input type="file" name="picture" size="30" value="上传图片"/><br/>
<input type="file" name="vedio" value="上传文本"/><br/>
<input type="submit" value="确定上传"/>
</form>
1.2 Servlet处理类
不管HTTP请求正文为何种数据类型,Servlet容器都会把HTTP请求包装成HttpServletRequest对象,但是解析过程将会是一项非常麻烦的事情。所以,我们可以利用Apache开源的两个jar包来实现文件的上传和下载, 它们的jar包如下图所示
(1)fileupload包:负责实现上传文件的软件包
(2)io包:负责输入输出的软件包
FileItemFactory是创建FileItem对象的工厂,DiskFileItemFactory类和DiskFileItem类分别实现了FileItemFactory接口和FileItem接口。
DiskFileItem类表示基于硬盘的FileItem,DiskFileItem类能够把客户端上传的文件数据保存到硬盘上,DiskFileItemFactory则是创建DiskFileItem对象的工厂。
下面的程序代码创建了DiskFileItemFactory对象,然后设置了硬盘写数据时所用的缓冲区大小,以及所使用的临时目录。为了提高向硬盘写数据的效率,尤其是在写大容量的时候会使用缓存,以及向临时目录存放一些数据。
//1、创建一个基于硬盘的FileItem工厂
DiskFileItemFactory factory = new DiskFileItemFactory();
//2、设置向硬盘写数据时所用缓冲区的大小,此处为5K
factory.setSizeThreshold(5*1024);
//3、设置上传时生成的临时文件的保存目录
factory.setRepository(new File(存放目录路径));
ServletFileUpload类为文件上传处理器,它域FileItemFactory关联。通过该类的setSizeMax()方法可以用来设置允许上传文件的大小。
//4、创建文件上传处理器
ServletFileUpload upload = new ServletFileUpload(factory);
upload.setHeaderEncoding("UTF-8"); //设置上传文件的编码方式为UTF-8
//5、设置运行上传文件的最大尺寸,此处为2M
upload.setSizeMax(2*1024*1024);
ServletFileUpload类的parseRequest(HttpServletRequest request)方法能够解析Request对象中的复合表单数据,返回一组FileItem对象的List集合
//6、获取包含文件对象的请求参数的List集合
List items = upload.parseRequest(request);
在得到了FileItem对象的List集合之后,通过遍历该集合里面的所有对象,通过该对象的类型是普通表单类型还是上传文件类型,从而执行不同的方法。
Iterator iter = items.iterator();
while(iter.hasNext()){
FileItem item = (FileItem)iter.next();
if(item.isFormField()){
//处理普通表单域
}else{
//处理上传文件域
}
}
最终的成品程序如下所示
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.PrintWriter;
import java.util.Iterator;
import java.util.List;
import javax.servlet.ServletConfig;
import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.apache.commons.fileupload.FileItem;
import org.apache.commons.fileupload.disk.DiskFileItemFactory;
import org.apache.commons.fileupload.servlet.ServletFileUpload;
public class UploadDemo extends HttpServlet {
private String savePath; // 文件存放路径
private File file; //存放目录
PrintWriter out; // 响应输出对象
public void init() throws ServletException {
//设置或获取文件保存的 目录,将上传的文件存放于WEB-INF目录下,不允许外界直接访问,保证上传文件的安全
savePath = getServletContext().getRealPath("/WEB-INF/upload");
System.out.println(savePath);
//获取该文件夹对象
file = new File(savePath);
//判断文件 存储目录是否存在,如果不存在就创建一个
if (!file.exists() && !file.isDirectory()) {
System.out.println(savePath+"目录不存在,需要创建");
//创建目录
file.mkdir();
}
}
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
doPost(request, response);
}
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
request.setCharacterEncoding("utf-8");
response.setContentType("text/html;charset=utf-8");
out = response.getWriter();
try{
//使用Apache文件上传组件处理文件上传步骤:
//1、创建一个基于硬盘的FileItem工厂
DiskFileItemFactory factory = new DiskFileItemFactory();
//2、设置向硬盘写数据时所用缓冲区的大小,此处为5K
factory.setSizeThreshold(5*1024);
//3、设置上传时生成的临时文件的保存目录
factory.setRepository(file);
//4、创建文件上传处理器
ServletFileUpload upload = new ServletFileUpload(factory);
upload.setHeaderEncoding("UTF-8"); //设置上传文件的编码方式为UTF-8
//5、设置运行上传文件的最大尺寸,此处为2M
upload.setSizeMax(2*1024*1024);
//6、获取包含文件对象的请求参数的List集合
List items = upload.parseRequest(request);
Iterator iter = items.iterator();
while(iter.hasNext()){
FileItem item = (FileItem)iter.next();
if(item.isFormField()){
//处理普通表单域
FormField(item);
}else{
//处理上传文件域
UploadFile(item);
}
}
upload.setHeaderEncoding("UTF-8");
}catch(Exception ex){
ex.printStackTrace();
}
}
private void FormField(FileItem item) throws Exception{
String name = new String(item.getFieldName().getBytes("ISO8859-1"),"UTF-8");; //获取参数名
String value = new String(item.getString().getBytes("ISO8859-1"),"UTF-8"); //获取普通参数值
out.print("<h3>获取参数 "+name+" : "+value+"</h3>");
}
private void UploadFile(FileItem item) throws Exception{
String fileName = item.getName(); //获取包含路径在内的文件名
int index = fileName.lastIndexOf("\\"); //获取最后一个斜杠的索引
//截取文件路径的字符串,截取文件名
fileName = fileName.substring(index+1, fileName.length());
long fileSize = item.getSize(); //获取文件大小
if(fileName.equals("") && fileSize == 0){
out.print("<h3>上传的文件不符合格式</h3>");
return;
}
File uploadFile = new File(savePath+"/"+fileName);
item.write(uploadFile); //将上传的文件写入(保存)到本地硬盘的指定路径中
out.print("<h3>成功保存文件:"+fileName+", 该文件大小为:"+fileSize+"byte</h3>");
}
}
1.3 最终效果
二、文件下载
在实际开发中,所有下载文件的信息都会存放在数据库当中,例如文件名、存放目录等,通过查询文件表就能获取文件的文件名和存放目录。在本示例中,我省去了这一步,直接在页面中指定下载文件名和存放目录。
2.1 JSP页面
<!DOCTYPE HTML>
<html>
<head>
<title>下载文件显示页面</title>
</head>
<body>
wenben3.txt<a href="/06-Upload/DownloadDemo?filename=wenben3.txt">下载</a>
wenben4.txt<a href="/06-Upload/DownloadDemo?filename=wenben4.txt">下载</a>
wenben2.txt<a href="/06-Upload/DownloadDemo?filename=wenben2.txt">下载</a>
wenben1.txt<a href="/06-Upload/DownloadDemo?filename=wenben1.txt">下载</a>
</body>
</html>
2.2 Servlet下载业务类
public void doGet(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
//获取想要下载的文件名
String fileName = request.getParameter("filename");
//将文件名进行转码,防止出现中文乱码
fileName = new String(fileName.getBytes("iso8859-1"),"UTF-8");
//设置文件所在路径(context对象的getRealPath方法能够获得Web应用的所在目录)
//getRealPath方法能够获得项目的运行目录,通过给定字符串能够获得包含给定虚拟路径的绝对路径
String path = this.getServletContext().getRealPath("/WEB-INF/upload")+"/"+fileName;
System.out.println(path);
//创建File对象,通过指定路径获取想要下载的文件
File file = new File(path);
//如果文件不存在
if(!file.exists()){
request.setAttribute("message", "该资源不存在!");
request.getRequestDispatcher("/message.jsp").forward(request, response);
return;
}
//设置响应正文的MIME类型,控制浏览器下载该文件
response.setHeader("content-disposition", "attachment;filename=" + fileName);
//读取要下载的文件,保存到文件输入流
FileInputStream in = new FileInputStream(path);
//创建输出流
OutputStream out = response.getOutputStream();
//创建缓冲区
byte buffer[] = new byte[1024];
int len = 0;
//循环将输入流中的内容读取到缓冲区当中
while((len=in.read(buffer))>0){
//输出缓冲区的内容到浏览器,实现文件下载
out.write(buffer, 0, len);
}
in.close(); //关闭文件输入流
out.close(); //关闭输出流
}
2.3 最终效果图