说明位于src类下面
Apache开源组织提供的处理文件上传的组件:
commons-fileupload-1.2.1.jar
commons-io-1.4.jar
简单步骤,处理上传文件数据
第1步,首先new 一个未经过配置的工厂实例DiskFileItemFactory
第2步,用提供的工厂创建一个解析上传文件的解析器实例ServletFileUpload!!
第3步,用解析器的parseRequest方法,解析request中提交的数据
每个表单项都封装成一个FileItem对象,加入list集合!
第4步,迭代list集合
第5步,isFormField判断普通字段还是上传字段
如果是上传字段getInputStream,一顿狂写
下面是十大上传文件的细节
1.上传文件的中文乱码
1.1 解决上传文件的文件名的中文乱码
ServletFileUpload.setHeaderEncoding("UTF-8")
1.2 解决普通输入项的值的中文乱码
(注意,表单类型为multipart/form-data的时候,设置request的编码是无效的)
FileItem.setString("UTF-8"); //解决乱码
2.在处理表单之前,要记得调用:
ServletFileUpload.isMultipartContent 静态方法判断提交表单的类型,
如果该方法返回true,则按上传方式处理,否
则按照传统方式处理表单即可。request.getParameter("username")...
3.设置解析器缓冲区的大小,以及临时文件的删除
设置解析器缓冲区的大小 DiskFileItemFactory.setSizeThreshold(1024*1024);
超过临界大小,需设置临时目录:DiskFileItemFactory.setRepository(new File(this.getServletContext().getRealPath("/temp")));
临时文件的删除:在程序中处理完上传文件后,一定要记得调用item.delete()方法,以删除临时文件,必须是在关闭流之后,finally代码块中,确保删除成功!
4.在做上传系统时,千万要注意上传文件的保存目录,这个上传文件的保存目录绝对不能让外界直接访问到。放到WEB-INF中保护起来!避免别人上传jsp脚本直接运行
5.限制上传文件的类型
在处理上传文件时,判断上传文件的后缀名是不是允许的
6.限制上传文件的大小
调用解析器的ServletFileUpload.setFileSizeMax(1024*1024*5);就可以限制上传文件的大小,如果上传文件超出限制,则解析器会抛FileUploadBase.FileSizeLimitExceededException异常,程序员通过是否抓到这个异常,进而就可以给用户友好提示。
7.如何判断空的上传输入项
String filename = item.getName().substring(item.getName().lastIndexOf("\\")+1); //""
if(filename==null || filename.trim().equals("")){
continue;
}
8、为避免上传文件的覆盖,程序在保存上传文件时,要为每一个文件生成一个唯一的文件名
public String generateFileName(String filename){
//83434-83u483-934934
return UUID.randomUUID().toString() + "_" + filename;
}
9、为避免在一个文件夹下面保存超过1000个文件,影响文件访问性能,程序应该把上传文件打散后存储。
public String generateSavePath(String path,String filename){
int hashcode = filename.hashCode(); //121221
int dir1 = hashcode&15;
int dir2 = (hashcode>>4)&0xf;
String savepath = path + File.separator + dir1 + File.separator + dir2;
File file = new File(savepath);
if(!file.exists()){
file.mkdirs();
}
return savepath;
}
10、监听上传进度
ServletFileUpload upload = new ServletFileUpload(factory);
upload.setProgressListener(new ProgressListener(){
public void update(long pBytesRead, long pContentLength, int pItems) {
System.out.println("当前已解析:" + pBytesRead);
}
});
高级版本
//细节:上传进度,前台用AJAX,后台注册一个监听器
ProgressListener lisen=new ProgressListener(){
//实现每1M更新一次,算法经典~
private long flag=-1;
public void update(long bytesRead, long contentLength, int items) {
//contentLength表示所有文件总大小
long full=bytesRead/(1000*1000);
if (flag==full) {
//开始时尚不足1M时,full=0,flag=-1,不作任何处理
return;
}
//当full每满1M时,将flag=full,便于下1M的时候判断
flag=full;
System.out.println("正在处理第"+items+"个文件");
//items从1开始算起~
if (contentLength==-1) {
System.out.println("总共上传了:"+bytesRead+"字节");
} else {
System.out.println("已上传了:"+bytesRead+"of"+contentLength+"字节");
}
}
};
//别忘记给upload设置一个监听器
upload.setProgressListener(lisen);
11、在web页面中添加动态上传输入项
12、下载时解决中文乱码
//下载之前,要先设置response的头字段,
//通知浏览器,以下载文件的方式打开,
//注意,解决文件名的中文乱码(用URLEncoder指定utf-8编码)
//注意,不能倒着取,因为有可能文件名中也有下划线XX-XX-XX_a_1.txt
//得到文件的原始文件名simpleName如 a_1.txt
String simpleName=full_name.substring(full_name.indexOf("_")+1);
//重点!通知浏览器以下载方式打开下面发送的数据
response.setHeader("content-disposition","attachment;filename="+URLEncoder.encode(simpleName, "utf-8"));
UploadServlet1位于web.controller包
package cn.itcast.web.controller;
import java.io.IOException;
import java.io.InputStream;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
public class UploadServlet extends HttpServlet {
public void doGet(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
/*如果表单的enctype属性值为multipart/form-data的话,
在servlet中注意就不能采用getParameter传统方式获取数据
那样,只会返回null
String username=request.getParameter("username");
System.out.println(username);null*/
InputStream in=request.getInputStream();
byte[] buf=new byte[1024];
int len=0;
while((len=in.read(buf))!=-1){
System.out.println(new String(buf,0,len));
}
in.close();
/*下面是表单的enctype属性值为multipart/form-data的时候Post体的值
-----------------------------7de1f327061c
Content-Disposition: form-data; name="username"
sg
-----------------------------7de1f327061c
Content-Disposition: form-data; name="file_1"; filename="a.txt"
Content-Type: text/plain
aaaaa
-----------------------------7de1f327061c
Content-Disposition: form-data; name="file_2"; filename="b.txt"
Content-Type: text/plain
bbbbb
-----------------------------7de1f327061c--*/
}
public void doPost(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
doGet(request, response);
}
}
/*MIME协议:
POST /day18/servlet/UploadServlet HTTP/1.1
Accept:
Referer: http://localhost:8080/day18/upload.jsp
Accept-Language: zh-cn
User-Agent: Mozilla/4.0
(compatible; MSIE 8.0; Windows NT 5.1; Trident/4.0; .NET CLR 2.0.50727)
Content-Type: multipart/form-data; boundary=---------------------------7de1282d74043a
Accept-Encoding: gzip, deflate
Host: localhost:8080
Content-Length: 440
Connection: Keep-Alive
Cache-Control: no-cache
Cookie: JSESSIONID=F05C1BDA9A447043CFB175DD4A5E8916
-----------------------------7de1282d74043a
Content-Disposition: form-data; name="username"
sg
-----------------------------7de1282d74043a
Content-Disposition: form-data; name="file_1"; filename="a.txt"
Content-Type: text/plain
aaaa
-----------------------------7de1282d74043a
Content-Disposition: form-data; name="file_2"; filename="b.txt"
Content-Type: text/plain
bbbbb
-----------------------------7de1282d74043a--
*/
UploadServlet2位于web.controller包
package cn.itcast.web.controller;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.util.List;
import javax.servlet.ServletException;
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.FileUploadException;
import org.apache.commons.fileupload.disk.DiskFileItemFactory;
import org.apache.commons.fileupload.servlet.ServletFileUpload;
public class UploadServlet2 extends HttpServlet {
public void doGet(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
//下面是简单地处理上传文件过程~
/*简单步骤,处理上传文件数据
第1步,首先new 一个未经过配置的工厂实例DiskFileItemFactory
第2步,用提供的工厂创建一个解析上传文件的解析器实例ServletFileUpload!!
第3步,用解析器的parseRequest方法,解析request中提交的数据
每个表单项都封装成一个FileItem对象,加入list集合!
第4步,迭代list集合
第5步,isFormField判断普通字段还是上传字段
如果是上传字段getInputStream,一顿狂写*/
//servlet的doPost方法try异常模板代码[try_post]
try {
//第1步,首先new 一个未经配置的工厂实例!
DiskFileItemFactory factory =new DiskFileItemFactory();
//第2步,用提供的工厂作参数,创建一个解析上传文件的解析器实例!
ServletFileUpload upload=new ServletFileUpload(factory);
//第3步,用解析器的parseRequest方法,解析request里的数据
//内部会将每个表单项都封装成一个FileItem对象,加入list集合!
List<FileItem> list=upload.parseRequest(request);
int i=0;
for (FileItem item : list) {
//先判断是否为简单的表单字段
if (item.isFormField()) {
//true表是 a simple form field
String fieldName=item.getFieldName();
//注意getString是针对普通字段,取其值的
String fieldValue=item.getString();
System.out.println("字段名:"+fieldName);
System.out.println("username:"+fieldValue);
} else {
//false代表是 an uploaded file
//代表当前处理的item里面封装的是上传文件
File dest=new File("C:\\upload\\");
//健壮性判断
if (!dest.exists()) {
dest.mkdirs();
}
//注意getName是针对上传文件,取其文件名的
// item.getName() 如果是IE6 C:\Documents and Settings\Admin\桌面\a.txt
// item.getName() 如果是IE7 a.txt
//所以要截取出来真正的上传文件名
String fileName=item.getName();
fileName=fileName.substring(fileName.lastIndexOf("\\")+1);
//字节流必须用字节数组
byte[] buf = new byte[1024];
int len = 0;
//getInputStream获取关联了上传文件内容的流
InputStream in=item.getInputStream();
//关联目的
OutputStream out = new FileOutputStream(new File("C:\\upload\\"+fileName));
//一顿狂写
while ((len = in.read(buf)) != -1) {
out.write(buf, 0, len);
}
//最后记得关流,实际开发时,放finally 还要try起来
in.close();
out.close();
}
}
} catch (Exception e) {
//出现异常不要紧,记录下来,并给友好提示!
e.printStackTrace();
request.setAttribute("message", "XX失败");
request.getRequestDispatcher("/message.jsp").forward(request,response);
}
//一路无错,就是上传成功
request.setAttribute("message", "上传成功");
request.getRequestDispatcher("/message.jsp").forward(request,response);
}
public void doPost(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
doGet(request, response);
}
}
/*String sql="C:\\Documents and Settings\\Administrator\\桌面\\a.txt";
System.out.println(sql.substring(sql.lastIndexOf("\\")+1));
结果是:a.txt
sql="b.txt";
System.out.println(sql.substring(sql.lastIndexOf("\\")+1));
结果是:b.txt*/
UploadServlet3
位于
web.controller
包
package cn.itcast.web.controller;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.util.Arrays;
import java.util.List;
import java.util.UUID;
import javax.servlet.ServletException;
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.FileUploadException;
import org.apache.commons.fileupload.ProgressListener;
import org.apache.commons.fileupload.FileUploadBase.FileSizeLimitExceededException;
import org.apache.commons.fileupload.disk.DiskFileItemFactory;
import org.apache.commons.fileupload.servlet.ServletFileUpload;
/*简单步骤,处理上传文件数据
第1步,首先new 一个未经过配置的工厂实例DiskFileItemFactory
第2步,用提供的工厂创建一个解析上传文件的解析器实例ServletFileUpload!!
第3步,用解析器的parseRequest方法,解析request中提交的数据
每个表单项都封装成一个FileItem对象,加入list集合!
第4步,迭代list集合
第5步,isFormField判断普通字段还是上传字段
如果是上传字段getInputStream,一顿狂写*/
public class UploadServlet3 extends HttpServlet {
//成员标记仅用于判断上传的文件是不是全为空~
private boolean flag=false;
public void doGet(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
//下面是详细地处理上传文件过程~注意各类细节问题~
//定义一个List集合记住允许上传的文件后缀
//允许上传的文件后缀名如下:
List<String> types=Arrays.asList("jpg","bmp","gif","png","txt","avi","pdf","mkv","mp3");
try {
//第1步,首先new 一个未经配置的工厂实例!
DiskFileItemFactory factory =new DiskFileItemFactory();
//细节:解析器缓冲区的大小,如小于10K,直接写硬盘
//工厂设置上传文件缓冲区大小的临界值:单位是K,默认是10K
factory.setSizeThreshold(1024*1024);
//单位是字节,现在是1M
//上面设置了上传文件大小 的临界值是1兆,
//当文件大小超过1兆的时候,会先写到临时目录下
//当上传文件过大时,便会用到临时文件夹(应自己确保临时文件的删除)
factory.setRepository(new File(this.getServletContext().getRealPath("/temp")));
//代表当前web应用,给服务器看
//上传完后记得调用FileItem.delete()删除,必须是在关闭流之后,finally代码块中,确保删除成功!
//第2步,用提供的工厂作参数,创建一个解析上传文件的解析器实例!
ServletFileUpload upload=new ServletFileUpload(factory);
//正式解析之前,必须先设置单个文件的大小限制!5M
upload.setFileSizeMax(1024*1024*50);
//ServletFileUpload的静态方法isMultipartContent(request)
//判断request,是否为上传文件的表单(enctype="mutipart/form-data")
if(!ServletFileUpload.isMultipartContent(request)){
//细节:如果表单不是upload file,那么就可以按传统方式处理
//只有普通表单,request.setCharacterEncoding("utf-8")才有效果
request.setCharacterEncoding("utf-8");
String value=request.getParameter("username");
System.out.println(value);
return;
}
//细节:在解析request之前,必须先解决上传文件名的中文乱码问题
upload.setHeaderEncoding("UTF-8");
//细节:上传进度,前台用AJAX,后台注册一个监听器
ProgressListener lisen=new ProgressListener(){
//实现每1M更新一次,算法经典~
private long flag=-1;
public void update(long bytesRead, long contentLength, int items) {
//contentLength表示所有文件总大小
long full=bytesRead/(1000*1000);
if (flag==full) {
//开始时尚不足1M时,full=0,flag=-1,不作任何处理
return;
}
//当full每满1M时,将flag=full,便于下1M的时候判断
flag=full;
System.out.println("正在处理第"+items+"个文件");
//items从1开始算起~
if (contentLength==-1) {
System.out.println("总共上传了:"+bytesRead+"字节");
} else {
System.out.println("已上传了:"+bytesRead+"of"+contentLength+"字节");
}
}
};
//别忘记给upload设置一个监听器
upload.setProgressListener(lisen);
//第3步,用解析器的parseRequest方法,解析request里的数据
//内部会将每个表单项都封装成一个FileItem对象,加入list集合!
List<FileItem> list=upload.parseRequest(request);
//System.out.println(list.size()); 什么都不填写时 是1
int i=0;
for (FileItem item : list) {
//先判断是否为简单的表单字段
if (item.isFormField()) {
//true表是 a simple form field
String fieldName=item.getFieldName();
//注意getString是针对普通字段,取其值的
//getString指定码表,可解决普通表单字段的值的中文乱码问题
//解决普通表单字段的值的中文乱码问题(如input name="username",value="中国")
//String fieldValue=item.getString("UTF-8");
//上面指定码表的内部实现如下:
//inputValue = new String(inputValue.getBytes("iso8859-1"),"UTF-8");
//或者自己手动解决普通表单字段的值的中文乱码问题
String fieldValue=item.getString();
fieldValue=new String(fieldValue.getBytes("iso8859-1"),"utf-8");
System.out.println("字段名:"+fieldName);
System.out.println("username:"+fieldValue);
} else {
//注意getName是针对上传文件,取其文件名的
// item.getName() 如果是IE6 C:\Documents and Settings\Admin\桌面\a.txt
// item.getName() 如果是IE7 a.txt
//所以要截取出来真正的上传文件名
String fileName=item.getName();
//如果类型为file的input 没有内容,文件名就为空字符串!
//服务器处理的时候需要先判断文件名是否为空字符串!
//否则,流写数据的时候会抛FileNotFoundExcetpion,因为不能向目录写内容
fileName=fileName.substring(fileName.lastIndexOf("\\")+1);
if (fileName==null || "".equals(fileName.trim())) {
//继续处理下一个上传文件
continue;
}
//运行到此标志,证明有文件上传了~
flag=true;
//正式处理上传文件之前,先判断文件名和文件大小
//在这儿点号不用转义
//先获取上传的文件的后缀如txt
String ext=fileName.substring(fileName.lastIndexOf(".")+1);
//再判断允许的后缀集合是否包含,如不包含,则跳转到全局消息
if(!types.contains(ext)){
//如果上传文件的类型不对,存消息,转发,return;
request.setAttribute("message", "不支持后缀为."+ext+"的文件上传");
request.getRequestDispatcher("/message.jsp").forward(request,response);
return;
}
//false代表是 an uploaded file
//代表当前处理的item里面封装的是上传文件
//细节:处理重复文件问题,生成唯一的文件名,并且打散存储
//调用自定义方法:参数是a.txt,返回的全文件名是:X-X-X_a.txt
String full_name=generateFullName(fileName);
//下面是打散文件,分别存放到指定目录下(如/WEB-INF/upload目录)
//参数是全球唯一的full_name,形如X-X-X_a.txt
//在指定目录下,根据完整文件名的低四次,和次低四位生成文件夹
//返回绝对路径(即完整路径,含全文件名)
String parentPath=this.getServletContext().getRealPath("/WEB-INF/upload");
String fullPath=generateChildPath(parentPath,full_name);
InputStream in=null;
OutputStream out=null;
try {
//字节流必须用字节数组
byte[] buf = new byte[1024];
int len = 0;
//getInputStream获取关联了上传文件内容的流
in=item.getInputStream();
//关联目的
//out = new FileOutputStream(new File("C:\\upload\\"+fileName));
//文件所在目录,必须保护起来,坚决不能让外界访问
out = new FileOutputStream(new File(fullPath));
//一顿狂写
while ((len = in.read(buf)) != -1) {
out.write(buf, 0, len);
}
}finally {
if (in != null) {
try {
in.close();
} catch (IOException e) {
throw new RuntimeException("in关闭失败!");
}
in = null;
}
if (out != null) {
try {
out.close();
} catch (IOException e) {
throw new RuntimeException("out关闭失败!");
}
out = null;
}
//删除临时文件:在程序中处理完上传文件后,
//一定要记得调用item.delete()方法,以删除临时文件
//必须是在关闭流之后,finally代码块中,确保删除成功!
item.delete(); //虽然有时会自动删除临时文件
}
}
}
}catch( FileSizeLimitExceededException e){
//如果上传文件的大小超过限制,存消息,转发,return;
request.setAttribute("message", "不支持5M以上大小的文件上传");
request.getRequestDispatcher("/message.jsp").forward(request,response);
return;
}catch (Exception e) {
//出现异常不要紧,记录下来,并给友好提示!
e.printStackTrace();
request.setAttribute("message", "XX失败");
request.getRequestDispatcher("/message.jsp").forward(request,response);
}
if( !flag){
request.setAttribute("message", "您没有上传任何文件~");
request.getRequestDispatcher("/message.jsp").forward(request,response);
return;
}
//一路无错,就是上传成功
request.setAttribute("message", "上传成功");
request.getRequestDispatcher("/message.jsp").forward(request,response);
}
//下面是打散文件,分别存放到指定目录下(如/WEB-INF/upload目录)
//参数是全球唯一的full_name,形如X-X-X_a.txt
//在指定目录下,根据完整文件名的低四次,和次低四位生成文件夹
//根据父目录和完整文件名称,返回绝对路径(即完整路径,含全文件名)
private String generateChildPath(String parentPath, String full_name) {
int hashCode=full_name.hashCode();//相同字符,哈希值一样,共4*8=32位
int dir1=hashCode&15;
int dir2=(hashCode>>4)&0xf;
//这样就可以保存16*1000*16个文件,如果不够用,还可以dir3
//因为部署到Linux系统,所以要做到与平台无关File.separator\
File file=new File(parentPath+File.separator+dir1+File.separator+dir2);
//健壮性判断
if (!file.exists()) {
//注意多层目录必须加S
file.mkdirs();
}
return file+File.separator+full_name;
}
//细节:处理重复文件问题,生成唯一的文件名,
//自定义方法1:参数是a.txt,返回的全文件名是:X-X-X_a.txt
//d92569e5-cb97-4305-8cc1-b72a5d91551b_a.txt
private String generateFullName(String fileName) {
return UUID.randomUUID().toString()+"_"+fileName;
}
public void doPost(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
doGet(request, response);
}
}
/*String sql="C:\\Documents and Settings\\Administrator\\桌面\\a.txt";
System.out.println(sql.substring(sql.lastIndexOf("\\")+1));
结果是:a.txt
sql="b.txt";
System.out.println(sql.substring(sql.lastIndexOf("\\")+1));
结果是:b.txt*/
ListFilesServlet位于
web.controller
包
package cn.itcast.web.controller;
import java.io.File;
import java.io.IOException;
import java.util.LinkedHashMap;
import java.util.Map;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
//列出网站所有可供下载的文件
//关系是如何保存迭代出来的文件名
public class ListFilesServlet extends HttpServlet {
public void doGet(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
String parentPath=this.getServletContext().getRealPath("/WEB-INF/upload");
//迭代出upload目录下的所有文件,
//保存到map集合,key是全球唯一的full_name,形如X-X-X_a.txt
//value是singleName,形如a.txt
Map<String, String> map=new LinkedHashMap<String, String>();
//调用递归方法,迭代出目录下的所有文件
File dir=new File(parentPath);
listAllFiles_1(dir,map);
System.out.println(map);//{}
//健壮性判断
if (map.size()==0) {
request.setAttribute("message", "暂无文件可供下载");
request.getRequestDispatcher("/message.jsp").forward(request, response);
return;
}
//全部存到map之后,存到request带给jsp显示
request.setAttribute("map", map);
request.getRequestDispatcher("/WEB-INF/jsp/listfiles.jsp").forward(request, response);
}
private void listAllFiles_2(File file, Map<String, String> map) {
//老方的递归,先判断file是文件还是文件夹
//如果是文件夹,取出所有文件,继续递归~
//如果是文件,添加到map
if (!file.isFile()) {
File[] files=file.listFiles();
for (File f : files) {
listAllFiles_2(f,map);
}
} else {
//file.getName() 就是XX-XX-XX_a.txt
//value就是a.txt
//8347824284-343-343_a_b.txt
map.put(file.getName(), file.getName().substring(file.getName().indexOf("_")+1));
//<a href="/servlet?filename=文件在服务器的UUID名称">文件的原始文件名(A.TXT)</a>
}
}
private void listAllFiles_1(File dir, Map<String, String> map) {
//老毕的递归,直接列出dir下所有文件迭代,
//再判断是文件还是文件夹
//如果是文件夹,继续递归~
//如果是文件,添加到map
File[] files=dir.listFiles();
for (File f : files) {
if (!f.isFile()) {
listAllFiles_1(f,map);
} else {
map.put(f.getName(), f.getName().substring(f.getName().indexOf("_")+1));
}
}
}
public void doPost(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
doGet(request, response);
}
}
DownLoadServlet位于web.controller包
package cn.itcast.web.controller;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.URLEncoder;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
public class DownLoadServlet extends HttpServlet {
public void doGet(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
//接收listfiles页面中发来的请求(注意get方式,中文乱码要手动解决)
//request.setCharacterEncoding("utf-8");仅针对post提交有效
//得到要下载的文件名 uuid文件名
String full_name=request.getParameter("full_name");
//URL后参数形式提交过来的中文,只能手工转~
full_name=new String(full_name.getBytes("iso8859-1"),"utf-8");
//调用方法,反向根据full_name得出文件在upload父目录下的哪个子目录
//父目录例如/WEB-INF/upload目录
//参数是全球唯一的full_name,形如X-X-X_a.txt
//在指定目录下,根据完整文件名的低四次,和次低四位找到文件夹
//返回绝对路径(即完整路径,含全文件名)
String parentPath=this.getServletContext().getRealPath("/WEB-INF/upload");
String fullPath=generateChildPath(parentPath,full_name);
File file=new File(fullPath);
//得到fullPath,先别急着提供下载,先进行健壮性判断
if (!file.exists()) {
//如果文件不存在,存消息,跳,返回
request.setAttribute("message", "您来晚了,资源已被销毁");
request.getRequestDispatcher("/message.jsp").forward(request,response);
return;
}
//一路OK,这时才提供下载
//下载之前,要先设置response的头字段,
//通知浏览器,以下载文件的方式打开,
//注意,解决文件名的中文乱码(用URLEncoder指定utf-8编码)
//注意,不能倒着取,因为有可能文件名中也有下划线XX-XX-XX_a_1.txt
//得到文件的原始文件名simpleName如 a_1.txt
String simpleName=full_name.substring(full_name.indexOf("_")+1);
//重点!通知浏览器以下载方式打开下面发送的数据
response.setHeader("content-disposition","attachment;filename="+URLEncoder.encode(simpleName, "utf-8"));
InputStream in = null;
OutputStream out = null;
try {
byte[] buf = new byte[1024];
int len = 0;
in=new FileInputStream(file);
out=response.getOutputStream();
while ((len = in.read(buf)) != -1) {
out.write(buf, 0, len);
}
} catch (IOException e) {
e.printStackTrace();
} finally {
if (in != null) {
try {
in.close();
} catch (IOException e) {
throw new RuntimeException("in关闭失败!");
}
in = null;
}
if (out != null) {
try {
out.close();
} catch (IOException e) {
throw new RuntimeException("out可以不用管,浏览器销毁response时,自动关流!");
}
out = null;
}
}
}
private String generateChildPath(String parentPath, String full_name) {
//调用方法,反向根据full_name得出文件在upload父目录下的哪个子目录
//父目录例如/WEB-INF/upload目录
//参数是全球唯一的full_name,形如X-X-X_a.txt
//在指定目录下,根据完整文件名的低四次,和次低四位找到文件夹
//返回绝对路径(即完整路径,含全文件名)
int hashCode=full_name.hashCode();
int dir1=hashCode&15;
int dir2=(hashCode>>4)&0xf;
return parentPath+File.separator+dir1+File.separator+dir2+File.separator+full_name;
}
public void doPost(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
doGet(request, response);
}
}
UploadServlet3_Copy位于web.controller包
package cn.itcast.web.controller;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.util.Arrays;
import java.util.List;
import javax.servlet.ServletException;
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.FileUploadException;
import org.apache.commons.fileupload.FileUploadBase.FileSizeLimitExceededException;
import org.apache.commons.fileupload.disk.DiskFileItemFactory;
import org.apache.commons.fileupload.servlet.ServletFileUpload;
/*简单步骤,处理上传文件数据
第1步,首先new 一个未经过配置的工厂实例DiskFileItemFactory
第2步,用提供的工厂创建一个解析上传文件的解析器实例ServletFileUpload!!
第3步,用解析器的parseRequest方法,解析request中提交的数据
每个表单项都封装成一个FileItem对象,加入list集合!
第4步,迭代list集合
第5步,isFormField判断普通字段还是上传字段
如果是上传字段getInputStream,一顿狂写*/
public class CopyOfUploadServlet3 extends HttpServlet {
public void doGet(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
//下面是详细地处理上传文件过程~注意各类细节问题~
//允许上传的文件后缀名如下:
List<String> types=Arrays.asList("jpg","bmp","gif","png","txt","avi","pdf");
try {
//第1步,首先new 一个未经配置的工厂实例!
DiskFileItemFactory factory =new DiskFileItemFactory();
//细节:解析器缓冲区的大小
factory.setSizeThreshold(1024*1024);//单位是字节,现在是1M
//当上传文件过大时,便会用到临时文件夹(应自己确保临时文件的删除)
factory.setRepository(new File(this.getServletContext().getRealPath("/temp")));
//第2步,用提供的工厂作参数,创建一个解析上传文件的解析器实例!
ServletFileUpload upload=new ServletFileUpload(factory);
//正式解析之前,必须先设置单个文件的大小限制!5M
upload.setFileSizeMax(1024*1024*5);
if(!ServletFileUpload.isMultipartContent(request)){
//细节:如果表单不是upload file,那么就可以按传统方式处理
//只有普通表单,request.setCharacterEncoding("utf-8")才有效果
request.setCharacterEncoding("utf-8");
String value=request.getParameter("username");
System.out.println(value);
return;
}
//细节:在解析request之前,必须先解决上传文件名的中文乱码问题
upload.setHeaderEncoding("UTF-8");
//第3步,用解析器的parseRequest方法,解析request里的数据
//内部会将每个表单项都封装成一个FileItem对象,加入list集合!
List<FileItem> list=upload.parseRequest(request);
int i=0;
for (FileItem item : list) {
//先判断是否为简单的表单字段
if (item.isFormField()) {
//true表是 a simple form field
String fieldName=item.getFieldName();
//注意getString是针对普通字段,取其值的
//getString指定码表,可解决普通表单字段的值的中文乱码问题
//String fieldValue=item.getString("UTF-8");
//或者自己手动解决普通表单字段的值的中文乱码问题
String fieldValue=item.getString();
fieldValue=new String(fieldValue.getBytes("iso8859-1"),"utf-8");
System.out.println("字段名:"+fieldName);
System.out.println("username:"+fieldValue);
} else {
//注意getName是针对上传文件,取其文件名的
// item.getName() 如果是IE6 C:\Documents and Settings\Admin\桌面\a.txt
// item.getName() 如果是IE7 a.txt
//所以要截取出来真正的上传文件名
String fileName=item.getName();
fileName=fileName.substring(fileName.lastIndexOf("\\")+1);
if (fileName==null || "".equals(fileName.trim())) {
continue;
}
//正式处理上传文件之前,先判断文件名和文件大小
//在这儿点号不用转义
String ext=fileName.substring(fileName.lastIndexOf(".")+1);
if(!types.contains(ext)){
//如果上传文件的类型不对,存消息,转发,return;
request.setAttribute("message", "不支持后缀为."+ext+"的文件上传");
request.getRequestDispatcher("/message.jsp").forward(request,response);
return;
}
//false代表是 an uploaded file
//代表当前处理的item里面封装的是上传文件
/*File dest=new File("C:\\upload\\");
//健壮性判断
if (!dest.exists()) {
dest.mkdirs();
}*/
InputStream in=null;
OutputStream out=null;
try {
//字节流必须用字节数组
byte[] buf = new byte[1024];
int len = 0;
//getInputStream获取关联了上传文件内容的流
in=item.getInputStream();
//关联目的
//out = new FileOutputStream(new File("C:\\upload\\"+fileName));
//文件所在目录,必须保护起来,坚决不能让外界访问
out = new FileOutputStream(new File(this.getServletContext().getRealPath("/WEB-INF/upload")+"\\"+fileName));
//一顿狂写
while ((len = in.read(buf)) != -1) {
out.write(buf, 0, len);
}
}finally {
if (in != null) {
try {
in.close();
} catch (IOException e) {
throw new RuntimeException("in关闭失败!");
}
in = null;
}
if (out != null) {
try {
out.close();
} catch (IOException e) {
throw new RuntimeException("out关闭失败!");
}
out = null;
}
//删除临时文件:在程序中处理完上传文件后,
//一定要记得调用item.delete()方法,以删除临时文件
//必须是在关闭流之后,finally代码块中,确保删除成功!
item.delete();
}
}
}
}catch( FileSizeLimitExceededException e){
//如果上传文件的大小超过限制,存消息,转发,return;
request.setAttribute("message", "不支持5M以上大小的文件上传");
request.getRequestDispatcher("/message.jsp").forward(request,response);
return;
}catch (Exception e) {
//出现异常不要紧,记录下来,并给友好提示!
e.printStackTrace();
request.setAttribute("message", "XX失败");
request.getRequestDispatcher("/message.jsp").forward(request,response);
}
//一路无错,就是上传成功
request.setAttribute("message", "上传成功");
request.getRequestDispatcher("/message.jsp").forward(request,response);
}
public void doPost(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
doGet(request, response);
}
}
/*String sql="C:\\Documents and Settings\\Administrator\\桌面\\a.txt";
System.out.println(sql.substring(sql.lastIndexOf("\\")+1));
结果是:a.txt
sql="b.txt";
System.out.println(sql.substring(sql.lastIndexOf("\\")+1));
结果是:b.txt*/
substringtest位于junit.test包
package junit.test;
public class substringtest {
public static void main(String[] args){
String sql="C:\\Documents and Settings\\Administrator\\桌面\\a.txt";
System.out.println(sql.substring(sql.lastIndexOf("\\")+1));
//a.txt
sql="b.txt";
System.out.println(sql.substring(sql.lastIndexOf("\\")+1));
//b.txt
}
}
toLowerCase位于junit.test包
package junit.test;
import java.io.File;
import org.junit.Test;
public class toLowerCase {
@Test
public void test(){
System.out.println("09809098098jpgAD.JPG".toLowerCase());
//09809098098jpgad.jpg
}
@Test
public void test2(){
System.out.println(File.pathSeparator);
//;
System.out.println(File.separator);
//\
}
}
index.jsp
<%@ page language="java" import="java.util.*" pageEncoding="utf-8"%>
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
<html>
<head>
<title>My JSP 'index.jsp' starting page</title>
</head>
<body>
<a href="/day18/upload.jsp">上传文件</a>
<a href="/day18/upload2.jsp">动态上传文件</a>
<a href="/day18/servlet/ListFilesServlet">列出所有上传文件</a>
<form action="${pageContext.request.contextPath }/servlet/UploadServlet3" method="post" >
上传用户:<input type="text" name="username"><br/>
<input type="submit" value="普通表单" />
</form>
</body>
</html>
upload.jsp
<%@ page language="java" import="java.util.*" pageEncoding="utf-8"%>
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
<html>
<head>
<title>上传页面</title>
</head>
<body>
<form action="${pageContext.request.contextPath }/servlet/UploadServlet3" method="post" enctype="multipart/form-data">
上传用户:<input type="text" name="username"><br/>
上传文件1:<input type="file" name="file_1"><br/>
上传文件2:<input type="file" name="file_2"><br/>
<input type="submit" value="开始上传" />
</form>
</body>
</html>
upload2.jsp
<%@ page language="java" import="java.util.*" pageEncoding="utf-8"%>
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
<html>
<head>
<title>动态添加file</title>
<script type="text/javascript">
function addFile(){
var div=document.getElementById("div_id");
//alert(div);
//创建input type为file
var file=document.createElement("input");
file.type="file";
//千万要有名字~
file.name="XXX";
//创建input type为button
var btn=document.createElement("input");
btn.type="button";
btn.value="删除";
btn.οnclick=function del(){
//父是小div,小div的父把小div删除掉~
this.parentNode.parentNode.removeChild(this.parentNode);
}
//创建个小的div
var small_div=document.createElement("div");
//黄鹰抓住了鹞子的脚,两个都扣了环
small_div.appendChild(file);
small_div.appendChild(btn);
div.appendChild(small_div);
}
</script>
</head>
<body>
<form action="${pageContext.request.contextPath }/servlet/UploadServlet3"method="post" enctype="multipart/form-data" >
<table border="1px">
<tr>
<td>用户名:</td>
<td><input type="text" name="username"></td>
</tr>
<tr>
<td><input type="button" value="添加上传" οnclick="addFile()"></td>
<td>
<div id="div_id"></div>
</td>
</tr>
<tr>
<td><input type="submit" value="开始上传"/></td>
</tr>
</table>
</form>
</body>
</html>
listfiles.jsp
<%@ page language="java" import="java.util.*" pageEncoding="utf-8"%>
<%@ taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c"%>
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
<html>
<head>
<title>显示所有文件列表</title>
</head>
<body>
注意两点:中文作为url地址后参数时,需要编码
url结合param可以作到这一点!同时会自动添加web应用名<br/>
下载文件有:<br/>
<c:forEach var="entry" items="${requestScope.map}">
<c:url var="url" value="/servlet/DownLoadServlet" scope="request">
<c:param name="full_name" value="${entry.key}"></c:param>
</c:url>
${entry.value }
<a href="${url }">下载</a><br/>
</c:forEach>
</body>
</html>