一、开发环境:Ecplise , jdk1.7 , web3.0
二、常见web工程
创建步骤在这里就不详细说了,相信大家都一定是会的,下面给大家看下工程的结构图并说一下所得jar包
工程目录图:
在这个工程所需3个jar包,分别是:commons-fileupload-1.3.1.jar、commons-io-2.4.jar、jstl-1.2.jar,这个三个jar很好下,也可以直接去Apache官网下载,等下我会把源码附上,里面会包含所需要的jar包。
三、实现思路
1.实例Apache提供的文件上传下载组件实现功能。
2.为防止文件覆盖的现象发生,要为上传文件产生一个唯一的文件名,我这里采用的是32位UUID。
3.为防止一个目录下面存放太多文件,需要将文件分来存放,因为一个文件夹下面存放着太多的文件,在获取文件的时候会耗费很长的时间,网上说有使用hash算法打散存储,我也试了这种储存方式,但是由于hash算法产生的目录是随机数据,我个人觉得并不适用在应用中,我在公司就处理过一次大批量文件的迁移,也就是前期赶项目的时候没有考虑太多,就把所有的文件都存放在一个目录下面了,导致我们的网站加载图片越来越慢了,解决方案是,图片文件按照大分类来存放,在分类文件下面,每年建一个文件夹,然后在年文件夹下面每月建一个文件夹,在月文件夹下面每天建一个文件夹,这样递归存放,就可以将文件分开存放,加载速度不会那么慢,执行完这个方案后,加载图片速度快多了。这里我就给大家讲一下这个思路,在demo的源码中就没有实现这个了,因为在存放文件,在下载的时候要利用数据库来获取相应的路径下载,demo的文件上传我全部放在某个文件夹的根目录下,方便后面的那个下载的功能。
4.要限制上传文件的最大值,这个很关键,要是上传一个很大的图片文件的话,用户在浏览网页的时候,加载时时间会很长,如果是APP的话,会耗费大量的流量,这是不可取的,一定的让运维对图片进行处理。
5、要限制上传文件的类型,在收到上传文件名时,判断后缀名是否合法,这个相信大家都懂,就不累赘的讲了,下面直接开刷源码吧。
四、实现源码解析
由于没有采用任何框架,就用原生的servlet了。
1.创建文件上传的servlet FileUploadServlet.java
package com.liujiang.servlet; import java.io.File; import java.io.FileOutputStream; import java.io.IOException; import java.io.InputStream; import java.util.List; import java.util.UUID; 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.FileUploadBase; import org.apache.commons.fileupload.ProgressListener; import org.apache.commons.fileupload.disk.DiskFileItemFactory; import org.apache.commons.fileupload.servlet.ServletFileUpload; /** * * @author Java文件上传 * */ @WebServlet("/FileUpload") public class FileUploadServlet extends HttpServlet { private static final long serialVersionUID = 1L; /** * 上传成功 */ private static final String SUCCESS = "文件上传结果:上传成功"; /** * 上传失败 */ private static final String ERROR = "文件上传结果:上传失败"; /** * 表单中无数据 */ private static final String FROM_EMPTY = "文件上传结果:表单中无数据"; /** * 单个文件超出最大值 */ private static final String FILE_SIZE_MAX = "文件上传结果:单个文件超出最大值"; /** * 总文件超出最大值 */ private static final String SIZE_MAX = "文件上传结果:总文件超出最大值"; /** * 文件上传结果JSP页面 */ private static final String MSG_JSP = "/message.jsp"; /** * 构造函数 */ public FileUploadServlet() { super(); } /** * get函数 */ protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { this.doPost(request, response); } /** * post函数 */ protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { //设置编码,解决表单数据乱码问题 request.setCharacterEncoding("UTF-8"); response.setCharacterEncoding("UTF-8"); //设置上传文件的存放目录,将上传的文件存放于WEB-INF目录下 String filePath = this.getServletContext().getRealPath("/WEB-INF/upload"); File file = new File(filePath); //判断上传文件的保存目录是否存在,不存在则创建 if (!file.exists() && !file.isDirectory()) { System.out.println(filePath+"目录不存在,需要创建"); //创建目录 file.mkdir(); } System.out.println("文件上传到的目录:" + filePath); //上传时生成的临时文件保存目录 String tempPath = this.getServletContext().getRealPath("/WEB-INF/temp"); File tmpFile = new File(tempPath); if (!tmpFile.exists()) { //创建临时目录 tmpFile.mkdir(); } //文件上传结果消息 String message = ""; try{ //使用Apache文件上传组件处理文件上传步骤: //创建一个DiskFileItemFactory工厂 DiskFileItemFactory factory = new DiskFileItemFactory(); //设置工厂的缓冲区的大小,当上传的文件大小超过缓冲区的大小时,就会生成一个临时文件存放到指定的临时目录当中。 //设置缓冲区的大小为100KB,如果不指定,那么缓冲区的大小默认是10KB factory.setSizeThreshold(1024*100); //设置上传时生成的临时文件的保存目录 factory.setRepository(tmpFile); //创建一个文件上传解析器 ServletFileUpload upload = new ServletFileUpload(factory); //监听文件上传进度 upload.setProgressListener(new ProgressListener(){ public void update(long pBytesRead, long pContentLength, int arg2) { System.out.println("文件大小为:" + pContentLength + ",当前已处理:" + pBytesRead); } }); //解决上传文件名的中文乱码 upload.setHeaderEncoding("UTF-8"); //判断提交上来的数据是否是上传表单的数据 if(!ServletFileUpload.isMultipartContent(request)){ //设置文件上传结果,并跳转至结果界面 request.setAttribute("message",FROM_EMPTY); request.getRequestDispatcher(MSG_JSP).forward(request, response); } //设置上传单个文件的大小的最大值,目前是设置为1024*1024*2字节,也就是2MB upload.setFileSizeMax(1024*1024*2); //设置上传文件总量的最大值,最大值=同时上传的多个文件的大小的最大值的和,目前设置为10MB upload.setSizeMax(1024*1024*10); //使用ServletFileUpload解析器解析上传数据,解析结果返回的是一个List<FileItem>集合,每一个FileItem对应一个Form表单的输入项 List<FileItem> list = upload.parseRequest(request); int i = 0; for(FileItem item : list){ //如果fileitem中封装的是普通输入项的数据 if(item.isFormField()){ String name = item.getFieldName(); //解决普通输入项的数据的中文乱码问题 String value = item.getString("UTF-8"); //普通数据这里就不处理了,直接打印出值 System.out.println(name + "=" + value); } //如果fileitem中封装的是上传文件 else{ i++; //得到上传的文件名称, String filename = item.getName(); System.out.println("上传第" + i + "个的文件名:" + filename); //文件为空,继续获取下一个文件 if(filename==null || filename.trim().equals("")){ continue; } //注意:不同的浏览器提交的文件名是不一样的,有些浏览器提交上来的文件名是带有路径的,如: c:\a\b\c.png,而有些只是单纯的文件名,如:c.png //处理获取到的上传文件的文件名的路径部分,只保留文件名部分 filename = filename.substring(filename.lastIndexOf(File.separator)+1); //得到上传文件的扩展名 String fileExtName = filename.substring(filename.lastIndexOf(".")+1); //如果需要限制上传的文件类型,那么可以通过文件的扩展名来判断上传的文件类型是否合法【按照实际情况,例如:图片,你就去校验一个扩展名是否是图片的扩展名】 System.out.println("上传的文件的扩展名是:"+fileExtName); //获取item中的上传文件的输入流 InputStream in = item.getInputStream(); //得到文件保存的名称 String saveFilename = makeFileName(filename); //创建一个文件输出流 FileOutputStream out = new FileOutputStream(filePath + File.separator + saveFilename); //创建一个缓冲区 byte buffer[] = new byte[1024]; //判断输入流中的数据是否已经读完的标识 int len = 0; //循环将输入流读入到缓冲区当中,(len=in.read(buffer))>0就表示in里面还有数据 while((len=in.read(buffer))>0){ //使用FileOutputStream输出流将缓冲区的数据写入到指定的目录(filePath + "\\" + filename)当中 out.write(buffer, 0, len); } //关闭输入流 in.close(); //关闭输出流 out.close(); //删除处理文件上传时生成的临时文件 item.delete(); //设置上传结果 message = SUCCESS; System.out.println(SUCCESS); } } }catch (FileUploadBase.FileSizeLimitExceededException e) { e.printStackTrace(); request.setAttribute("message", FILE_SIZE_MAX); System.out.println(FILE_SIZE_MAX); request.getRequestDispatcher(MSG_JSP).forward(request, response); return; }catch (FileUploadBase.SizeLimitExceededException e) { e.printStackTrace(); System.out.println(SIZE_MAX); request.setAttribute("message", SIZE_MAX); request.getRequestDispatcher(MSG_JSP).forward(request, response); return; }catch (Exception e) { //上传中,出现异常,请分析异常信息 message= ERROR; System.out.println(ERROR); e.printStackTrace(); return; } //设置文件上传结果,并跳转至结果界面 request.setAttribute("message",message); request.getRequestDispatcher(MSG_JSP).forward(request, response); } /** * * @param 获取唯一文件名,这里采用UUID生成 * @return */ private String makeFileName(String filename){ //为防止文件覆盖的现象发生,要为上传文件产生一个唯一的文件名 return UUID.randomUUID().toString() + "_" + filename; } }
对应的web.xml配置
<!-- 文件上传servlet 开始 --> <servlet> <servlet-name>FileUploadServlet</servlet-name> <servlet-class>com.liujiang.servlet.FileUploadServlet</servlet-class> </servlet> <servlet-mapping> <servlet-name>FileUploadServlet</servlet-name> <url-pattern>/fileUpload</url-pattern> </servlet-mapping> <!-- 文件上传servlet 结束-->
jsp页面:
文件上传的jsp
index.jsp
<%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%> <!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd"> <html> <head> <meta http-equiv="Content-Type" content="text/html; charset=UTF-8"> <title>JavaWeb文件上传Demo</title> </head> <body> <center> <h1>Java文件上传Demo</h1> <div> <!-- 文件上传的表单比普通的多个属性 enctype="multipart/form-data"--> <form id="form" action="fileUpload" class="form-horizontal" enctype="multipart/form-data" method="post"> <div class="form-group"> <label class="col-sm-2 control-label no-padding-right" for="seqNum"><font color="red">*</font>上传文件文件 </label> <div class="col-sm-8"> <div class="clearfix"> 上传文件1:<input type="file" name="file1"><br/> 上传文件2:<input type="file" name="file2"><br/> <input type="submit" value="提交"> </div> </div> </div> </form> </div> <br/> <br/> <br/> </center> </body> </html>
操作结果消息jsp
message.jsp
<%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%> <!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd"> <html> <head> <meta http-equiv="Content-Type" content="text/html; charset=UTF-8"> <title>文件上传结果</title> </head> <body> ${message} </body> </html>
文件上传的代码就结束了。
文件下载
首先得吧所有文件的显示在页面上
创建FileListVeiw servlet
package com.liujiang.servlet; import java.io.File; import java.io.IOException; import java.util.HashMap; import java.util.Map; import javax.servlet.ServletException; import javax.servlet.annotation.WebServlet; import javax.servlet.http.HttpServlet; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; /** * 文件列表servlet * @author Ocean * */ @WebServlet("/FileListVeiw") public class FileListVeiwServlet extends HttpServlet { private static final long serialVersionUID = 1L; /** * 构造函数 */ public FileListVeiwServlet() { super(); // TODO Auto-generated constructor stub } /** * get函数 */ protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { doPost(request, response); } /** * post函数 */ protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { //获取文件的所在目录 String uploadFilePath = this.getServletContext().getRealPath("/WEB-INF/upload"); System.out.println("文件的所在目录" + uploadFilePath); //存储要下载的文件名 Map<String,String> fileNameMap = new HashMap<String,String>(); //递归遍历filepath目录下的所有文件和目录,将文件的文件名存储到map集合中 fileList(new File(uploadFilePath),fileNameMap);//File既可以代表一个文件也可以代表一个目录 //将Map集合发送到listfile.jsp页面进行显示 request.setAttribute("filelist", fileNameMap); request.getRequestDispatcher("/fileList.jsp").forward(request, response); } /** * 递归遍历指定目录下的所有文件 * @param file 递归遍历指定目录下的所有文件 * @param map 存储文件名的Map集合 */ public void fileList(File file,Map<String,String> map){ //如果file代表的不是一个文件,而是一个目录 if(!file.isFile()){ //列出该目录下的所有文件和目录 File files[] = file.listFiles(); //遍历files[]数组 for(File f : files){ //递归 fileList(f,map); } }else{ String realName = file.getName().substring(file.getName().indexOf("_")+1); //file.getName()得到的是文件的原始名称,这个名称是唯一的,因此可以作为key,realName是处理过后的名称,有可能会重复 map.put(file.getName(), realName); } } }
创建显示的jsp页面,fileList.jsp
<%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%> <%@taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %> <!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd"> <html> <head> <meta http-equiv="Content-Type" content="text/html; charset=UTF-8"> <title>所有图片列表</title> </head> <body> <center> <h1>文件列表</h1> <!-- 遍历Map集合 --> <c:forEach items="${filelist}" var="fileName"> <c:url value="fileDownLoad" var="downurl"> <c:param name="filename" value="${fileName.key}"></c:param> </c:url>${fileName.value}<a href="${downurl}">下载</a> <br/> </c:forEach> </center> </body> </html>
效果:
文件下载:
创建文件下载 servlet FileDownLoadServletjava
package com.liujiang.servlet; import java.io.File; import java.io.FileInputStream; import java.io.IOException; import java.io.OutputStream; import java.net.URLEncoder; import javax.servlet.ServletException; import javax.servlet.annotation.WebServlet; import javax.servlet.http.HttpServlet; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; /** * 文件下载servlet * @author Ocean * */ @WebServlet("/FileDownLoadServlet") public class FileDownLoadServlet extends HttpServlet { private static final long serialVersionUID = 1L; /** * 构造函数 */ public FileDownLoadServlet() { super(); // TODO Auto-generated constructor stub } /** * get函数 */ protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { doPost(request, response); } /** * post函数 */ protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { //得到要下载的文件名 String fileName = request.getParameter("filename"); fileName = new String(fileName.getBytes("iso8859-1"),"UTF-8"); //上传的文件都是保存在/WEB-INF/upload目录下的子目录当中 String rootPath=this.getServletContext().getRealPath("/WEB-INF/upload"); //文件的物理路径,在实际的应用中会把根目录之下的文件路径存放在数据库中 String path = String.format("%s%s%s", rootPath,File.separator,fileName); //得到要下载的文件 File file = new File(path); //如果文件不存在 if(!file.exists()){ request.setAttribute("message", "您要下载的资源已被删除!!"); request.getRequestDispatcher("/message.jsp").forward(request, response); return; } //处理文件名 String realname = fileName.substring(fileName.indexOf("_")+1); //设置响应头,控制浏览器下载该文件 response.setHeader("content-disposition", "attachment;filename=" + URLEncoder.encode(realname, "UTF-8")); //读取要下载的文件,保存到文件输入流 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(); } }
效果图:
到此,整个demo就全部分享完了,如果有什么错误,欢迎大牛斧正,谢谢!!!
突然发现不能上传附件,如有需要demo的,直接留下邮箱吧,看到就给你发过去。