拷贝jar包:commons-fileupload.jar commons-io.jar 具体参考
http://commons.apache.org/proper/commons-fileupload/using.html
借助第三方组件,Commons-fileupload组件来实现文件的上传。 package com.itheima.servlet; import java.io.File; import java.io.FileNotFoundException; import java.io.FileOutputStream; import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; import java.io.PrintWriter; 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; import org.apache.commons.io.FilenameUtils; import com.sun.corba.se.impl.orb.ParserTable; //借助commons-fileupload 实现文件上传的简单案例 public class UploadServlet2 extends HttpServlet { public void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { //设置响应正文的编码格式,防止中文乱码 response.setContentType("text/html;charset=UTF-8"); PrintWriter out = response.getWriter(); //判断用户提交的正文类型是不是multipart/form-data类型 boolean isMultipart = ServletFileUpload.isMultipartContent(request); if(!isMultipart){ throw new RuntimeException("请检查您的表单的enctype属性,确定是multipart/form-data"); } //创建DiskFileItemFactory DiskFileItemFactory dfif = new DiskFileItemFactory(); ServletFileUpload parser = new ServletFileUpload(dfif); List<FileItem> items = null ; //解析内容 try { items = parser.parseRequest(request); } catch (FileUploadException e) { throw new RuntimeException("解析上传内容失败,请重新试一下"); } //处理请求内容 if(items != null){ for(FileItem item:items){ if(item.isFormField()){ //普通表单字段 processFormField(item); } else{ //上传的文件字段 processUploadField(item); } } out.write("上传成功"); } } //上传字段 private void processUploadField(FileItem item) { try { // InputStream in = item.getInputStream(); //输入流 String fileName = item.getName(); //上传文件的名称 C:\Users\zhangjianbo\Desktop\a.txt a.txt //截取文件名 // fileName = fileName.substring(fileName.lastIndexOf("\\")+1); if(fileName!=null){ fileName = FilenameUtils.getName(fileName); } //把文件存到服务器的某个目录中 String storeDirectoryPath = getServletContext().getRealPath("/files"); File storeDirectory = new File(storeDirectoryPath); if(!storeDirectory.exists()){ storeDirectory.mkdirs(); } // //构建输出流,输出流需要指定文件写出的路径 // OutputStream out = new FileOutputStream(storeDirectoryPath +File.separator+ fileName); // int len = -1; // byte b[] = new byte[1024]; // while((len = in.read(b))!=-1){ // out.write(b,0,len); // } // // in.close(); // out.close(); //item.delete(); //传递大文件的时候,会自动产生临时文件 item.write(new File(storeDirectoryPath +File.separator+ fileName));// File Item中存在write方法 } catch (Exception e) { throw new RuntimeException("上传失败,请重试"); } } //普通表单字段 private void processFormField(FileItem item) { String filedName = item.getFieldName(); //字段名 String filedValue = item.getString(); //字段值 System.out.println(filedName + "=" + filedValue); } public void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { doGet(request, response); } } 3.文件上传详解
public void setRepository(File repository):设置临时文件的存放目录
public void setSizeThreshold(int sizeThreshold):设置缓存的大小
专题:关于临时文件
虚拟内存的概念:当内存中的空间不够用的时候,这个时候,会在磁盘上划分一部分区域作为虚拟内存,将内存中不经常使用的文件先保存到虚拟内存中。所以虚拟内存是磁盘上的一部分存储空间。
为什么启用缓存呢?减少与硬盘的交互次数,最终还是要存放到磁盘上的 ,存放到内存中,速度更快。 所以上传的文件首先是存放到内存中的缓存中的,
如果是一个100M大小的文件,那么每次获取10Kb,到内存中,然后存放到临时文件中,当所有的100M都存放到临时文件中后,然后再把临时文件中的文件全部放到最终文件中,最后把临时文件进行删除。
临时文件需要在代码中指定来实现,调用item.delete()来删除。 FileItem item = new DiskFileItemFactory()
临时文件的删除问题:
文件上传时,自己用IO流处理的文件,一定要在流关闭的时候删除临时文件,FileItem.delete()
建议使用:FileItem.writer(File f)会自动删除临时文件
3.2乱码问题
1) 普通字段乱码
filedValue = item.getString("UTF-8"); 如果提交的是输出框的值,那么需要通过FileItem.getString(String charset);编码需要和客户端一致
2)文件名乱码
可以通过requset.setCharacterEncoding("UTF-8");仅能够解决报文中的请求头的问题,不能解决正文的编码问题
3)解决文件名重名的问题
通过UUID来命名文件名fileName = UUID.randomUUID()+"_"+ FilenameUtils.getName(fileName);
4)保证服务器的安全
为了防止用户上传的文件,通过路径可以直接访问到该文件,造成服务器的漏洞,bat文件等,需要将用户上传的文件存放到用户不能直接访问的地方,如:
String storeDirectoryPath = getServletContext().getRealPath("/WEB-INF/files"); 将文件放到WEB-INF目录下面。
5)避免一个文件夹中的文件过多
解决方法:分目录存储
参考实现:
1)按照日期分目录存储
2)通过文件名的HashCode来设定文件夹
//上传字段
private void processUploadField(FileItem item) {
try {
String fileName = item.getName(); //上传文件的名称 C:\Users\zhangjianbo\Desktop\a.txt a.txt
//截取文件名
if(fileName!=null){
fileName = UUID.randomUUID()+"_"+ FilenameUtils.getName(fileName);
}
// //分目录存储:日期解决方案
// Date now = new Date();
// DateFormat df = new SimpleDateFormat("yyyy-MM-dd");
// String time = df.format(now);
//通过文件名的hashCode来实现
String childDiretory = makeChildDirectory(getServletContext().getRealPath("/WEB-INF/files/"),fileName);
//把文件存到服务器的某个目录中
String storeDirectoryPath = getServletContext().getRealPath("/WEB-INF/files/"+childDiretory);
File storeDirectory = new File(storeDirectoryPath);
if(!storeDirectory.exists()){
storeDirectory.mkdirs();
}
item.delete();//表示删除临时文件
item.write(new File(storeDirectoryPath +File.separator+ fileName));// File Item中存在write方法
} catch (Exception e) {
throw new RuntimeException("上传失败,请重试");
}
}
private String makeChildDirectory(String realPath, String fileName) {
int hashCode = fileName.hashCode();
int dir1 = hashCode&0xf;//取文件名的二进制的1~4位
int dir2 = (hashCode&0xf0)>>4; //获取5~8
String directory = "" + dir1 + File.separator +dir2;
File file = new File(realPath,directory);
if (!file.exists()){
file.mkdirs();
}
return directory;
}
3.6 限制文件上传的大小,并给出友好提示
Web方式不适合传输较大的文件
1)单文件大小(一般限制4M)
//创建DiskFileItemFactory
DiskFileItemFactory dfif = new DiskFileItemFactory();
ServletFileUpload parser = new ServletFileUpload(dfif);
parser.setFileSizeMax(4*1024*1024); //设置单个文件上传的大小
parser.setSizeMax(6*1024*1024); //多文件上传时的总大小限制
List<FileItem> items = null ;
//解析内容
try {
items = parser.parseRequest(request);
}catch(FileUploadBase.FileSizeLimitExceededException e){ //该异常为一个静态内部类
out.write("上传的文件超出了4M");
return;
}
catch(FileUploadBase.SizeLimitExceededException e){ //该异常为一个静态内部类
out.write("总文件大小超出了6M");
return;
}
catch (FileUploadException e) {
throw new RuntimeException("解析上传内容失败,请重新试一下");
}
2)总文件大小(多文件上传)
parser.setSizeMax(6*1024*1024); //多文件上传时的总大小限制
3.7 限制上传文件的类型
通过扩展名+ 文件的MIME类型来进行限制
// 扩展名
String extension = FilenameUtils.getExtension(fileName);
// MIME类型 -- 判断MIME类型仅限于IE浏览器
String contentType = item.getContentType();
if (contentType.startsWith("image/")) {
MIME类型:Web请求文件中的Content-Type类型,就表示MIME类型
由大类型/小类型组成,我们只需要判断大类型即可。
4.文件的下载 public void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { String uuidfilename = request.getParameter("filename");//get方式提交的 uuidfilename = new String(uuidfilename.getBytes("ISO-8895-1"),"UTF-8");//UUID的文件名 String storeDirectory = getServletContext().getRealPath("/WEB-INF/files"); //得到存放的子目录 String childDirecotry = makeChildDirectory(storeDirectory,uuidfilename); //构建输出流 InputStream in = new FileInputStream(storeDirectory + File.separator + childDirecotry + File.separator + uuidfilename); //下载 String oldfilename = uuidfilename.substring(uuidfilename.indexOf("_") + 1); //通知客户端以下载的方式打开 response.setHeader("Content-Disposition", "attachment;filename="+URLEncoder.encode(oldfilename,"UTF-8")); OutputStream out = response.getOutputStream(); int len = -1; byte b[] = new byte[1024]; while((len = in.read(b))!= - 1){ out.write(b,0,len); } in.close(); out.close(); } private String makeChildDirectory(String realPath, String fileName) { int hashCode = fileName.hashCode(); int dir1 = hashCode & 0xf;// 取文件名的二进制的1~4位 int dir2 = (hashCode & 0xf0) >> 4; // 获取5~8 String directory = "" + dir1 + File.separator + dir2; File file = new File(realPath, directory); if (!file.exists()) { file.mkdirs(); } return directory; } public void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { doGet(request, response); } } =================jstl url ================== <h1>jstl中的url的编码方式,使用url编码,因为路径中有中文</h1> <c:forEach items="${map}" var = "me"> <c:url value = "/servlet/DownLoadServlet" var = "url"> <c:param name="filename" value="${me.key}"> </c:param> </c:url> ${me.value} <a href="${url}"></a>下载<br/> ==============showallfiles============= public class ShowAllFilesServlet extends HttpServlet { public void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { String storeDirectory = getServletContext().getRealPath("/WEB-INF/files"); File root = new File(storeDirectory); //使用Map保存递归的文件名,key:UUID文件名 value:老文件名 Map<String,String> map = new HashMap<String, String>(); treeWalker(root,map); request.setAttribute("map", map); request.getRequestDispatcher("/listFiles.jsp").forward(request, response); } //递归,将文件名存放到Map中 private void treeWalker(File root, Map<String, String> map) { if(root.isFile()){ String fileName = root.getName(); //获取当前的文件名 234322_a_a.bmp String oldFileName = fileName.substring(fileName.indexOf("_") + 1); map.put(fileName, oldFileName); } else{ File fs[] = root.listFiles(); for(File file:fs){ treeWalker(file, map); } } } 二、Servlet规范中的监听器 1.观察者设计模式 2.Servlet规范中提供的八个监听器 3.监听器案例:查看在线登录的人数,踢人