我们要将Web应用系统中的文件资源提供给用户进行下载,首先我们要有一个页面列出上传文件目录下的所有文件,当用户点击文件下载超链接时就进行下载操作,编写一个ListFileServlet,用于列出Web应用系统中所有下载文件 获取文件列表 package me.gacl.web.controller; import java.io.File; import java.io.IOException; import java.util.HashMap; 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 ListFileServlet extends HttpServlet { public void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { //获取上传文件的目录 String uploadFilePath = this.getServletContext().getRealPath("/WEB-INF/upload"); //存储要下载的文件名 Map<String,String> fileNameMap = new HashMap<String,String>(); //递归遍历filepath目录下的所有文件和目录,将文件的文件名存储到map集合中 listfile(new File(uploadFilePath),fileNameMap);//File既可以代表一个文件也可以代表一个目录 //将Map集合发送到listfile.jsp页面进行显示 request.setAttribute("fileNameMap", fileNameMap); request.getRequestDispatcher("/listfile.jsp").forward(request, response); } public void listfile(File file,Map<String,String> map){ //如果file代表的不是一个文件,而是一个目录 if(!file.isFile()){ //列出该目录下的所有文件和目录 File files[] = file.listFiles(); //遍历files[]数组 for(File f : files){ //递归 listfile(f,map); } }else{ /**
-
处理文件名,上传后的文件是以uuid_文件名的形式去重新命名的,去除文件名的uuid部分 file.getName().indexOf("")检索字符串中第一次出现"_"字符的位置,如果文件名类似于:9349249849-88343-8344_阿_凡达.avi 那么file.getName().substring(file.getName().indexOf("")+1)处理之后就可以得到阿_凡达.avi部分 */ String realName = file.getName().substring(file.getName().indexOf("")+1); //file.getName()得到的是文件的原始名称,这个名称是唯一的,因此可以作为key,realName是处理过后的名称,有可能会重复 map.put(file.getName(), realName); } } public void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { doGet(request, response); } } ListFileServlet中listfile方法,listfile方法是用来列出目录下的所有文件的,listfile方法内部用到了递归,在实际开发当中,我们肯定会在数据库创建一张表,里面会存储上传的文件名以及文件的具体存放目录,我们通过查询表就可以知道文件AxiTrader代理申请www.fx61.com/brokerlist/axitrader.html的具体存放目录,是不需要用到递归操作的,这个例子是因为没有使用数据库存储上传的文件名和文件的具体存放位置,而上传文件的存放位置又使用了散列算法打散存放,所以需要用到递归,在递归时,将获取到的文件名存放到从外面传递到listfile方法里面的Map集合当中,这样就可以保证所有的文件都存放在同一个Map集合当中。 配置 在Web.xml文件中配置ListFileServlet <servlet> <servlet-name>ListFileServlet</servlet-name> <servlet-class>me.gacl.web.controller.ListFileServlet</servlet-class> </servlet> <servlet-mapping> <servlet-name>ListFileServlet</servlet-name> <url-pattern>/servlet/ListFileServlet</url-pattern> </servlet-mapping> 下载页面 展示下载文件的listfile.jsp页面如下: <%@ page language="java" import="java.util.*" pageEncoding="UTF-8"%> <%@taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %> <!DOCTYPE HTML> <html> <head> <title>下载文件显示页面</title> </head> <body> <!-- 遍历Map集合 --> <c:forEach var="me" items="${fileNameMap}"> <c:url value="/servlet/DownLoadServlet" var="downurl"> <c:param name="filename" value="${me.key}"></c:param> </c:url> ${me.value}<a href="${downurl}">下载</a> <br/> </c:forEach> </body> </html> 实现文件下载 编写一个用于处理文件下载的Servlet,DownLoadServlet的代码如下: public class DownLoadServlet extends HttpServlet { public void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { //得到要下载的文件名 String fileName = request.getParameter("filename"); //23239283-92489-阿凡达.avi //上传的文件都是保存在/WEB-INF/upload目录下的子目录当中 String fileSaveRootPath=this.getServletContext().getRealPath("/WEB-INF/upload"); //通过文件名找出文件的所在目录 String path = findFileSavePathByFileName(fileName,fileSaveRootPath); //得到要下载的文件 File file = new File(path + "\" + fileName); //如果文件不存在 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 + "\" + fileName); //创建输出流 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(); } public String findFileSavePathByFileName(String filename,String saveRootPath){ int hashcode = filename.hashCode(); int dir1 = hashcode&0xf; //0--15 int dir2 = (hashcode&0xf0)>>4; //0-15 String dir = saveRootPath + "\" + dir1 + "\" + dir2; //upload\2\3 upload\3\5 File file = new File(dir); if(!file.exists()){ //创建目录 file.mkdirs(); } return dir; } public void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { doGet(request, response); } } 配置 <servlet> <servlet-name>DownLoadServlet</servlet-name> <servlet-class>me.gacl.web.controller.DownLoadServlet</servlet-class> </servlet> <servlet-mapping> <servlet-name>DownLoadServlet</servlet-name> <url-pattern>/servlet/DownLoadServlet</url-pattern> </servlet-mapping> 1.html5支持的文件下载 非常简单代码如下: <a download="下载文件.txt">下载文件</a> 这种方式不支持ie浏览器 2.需要后台支持的文件下载: 前端代码如下: function getRootPath() { //获取当前网址,如: http://localhost:8083/uimcardprj/share/meun.jsp var curWwwPath = window.document.location.href; //获取主机地址之后的目录,如: uimcardprj/share/meun.jsp var pathName = window.document.location.pathname; var pos = curWwwPath.indexOf(pathName); //获取主机地址,如: http://localhost:8083 var localhostPaht = curWwwPath.substring(0, pos); //获取带"/"的项目名,如:/uimcardprj var projectName = pathName.substring(0, pathName.substr(1).indexOf('/') + 1); return (localhostPaht + projectName); } function downloadExcel(fileName) { fileName = encodeURI(fileName); var url = getRootPath() + "/static/excel/" + fileName; window.location.href = "data/downloadexcel?fileName=" + fileName + "&url=" + url; } 此处需要传递下载文件的文件名称与文件的地址(为了避免服务器路径问题,此处直接使用前端传递的url路径地址,这种方式也可以用来下载第三方网站上的文件资源),注意对于文件名称需要处理文件名称中的中文字符与特殊字符需要使用js方法encodeURI对文件名称编码 后台代码: /**
- 下载文件
-
- @param fileName
- @param url
- @return
*/ @RequestMapping(value = "/index/download", method = RequestMethod.GET)@ResponseBody br/>@ResponseBody logger.info("下载文件:" + url); // 需要对url进行编码,默认情况下,只编码最后一个 / 之后的内容 int index = url.lastIndexOf("/") + 1; this.response.reset(); this.response.setContentType("multipart/form-data"); HttpURLConnection conn = null; InputStream inputStream = null; try { url = url.substring(0, index) + URLEncoder.encode(url.substring(index), "utf-8"); //注意URLEncoder.encode会将空格转换为+,需要做特殊处理 url = url.trim().replaceAll("\+", "%20"); this.response.setHeader("Content-Disposition", "attachment;fileName=" + processFileName(this.request, fileName)); URL url1 = new URL(url); conn = (HttpURLConnection) url1.openConnection(); inputStream = conn.getInputStream(); ServletOutputStream out = this.response.getOutputStream(); IOUtils.copy(inputStream, out); out.flush(); } catch (IOException e) { logger.error("下载模板文件出错", e); } finally { if (null != inputStream) { try { inputStream.close(); } catch (IOException e) { logger.error("下载模板文件出错", e); } } if (null != conn) { try { conn.disconnect(); } catch (Exception e) { logger.error("下载模板文件出错", e); } } } }
/**
- @Title: processFileName
-
@Description: ie, chrom, firfox下处理文件名显示乱码 */ public static String processFileName(HttpServletRequest request, String fileNames) { String codedfilename = null; try { String agent = request.getHeader("USER-AGENT"); if (null != agent && agent.indexOf("MSIE") > -1 || null != agent && agent.indexOf("Trident") > -1) {// ie String name = java.net.URLEncoder.encode(fileNames, "UTF8").replaceAll("\+","%20"); codedfilename = name; } else {// 火狐,chrome等
codedfilename = new String(fileNames.getBytes("UTF-8"),
"iso-8859-1");
}
} catch (Exception e) { logger.error("文件名称编码出错", e); } return codedfilename; } 下载文件代码需要注意的地方就只有文件名称的编码问题,其他代码很简单,需要特别注意URLEncoder.encode将空格转换为+,需要特殊处理转换为%20 3.下载后台实时生成的文件 简单的后台实时生成文件基本代码和步骤二一致,只是将从网络获取的文件改为从本机获取就行了 4.使用post方法下载文件 post方法下载文件主要可以通过两种方式解决 第一种将post提交到当前页面的隐藏iframe即可,但这种方式在ie和chmore中会有两种表现形式(一种直接在当前页面下载,一种会打开一个空白页下载) var downLoadFile = function (options) { var config = $.extend(true, {method: 'post'}, options); var $iframe = $('<iframe id="down-file-iframe" />'); var $form = $('<form target="down-file-iframe" method="' + config.method + '" />'); $form.attr('action', config.url); for (var key in config.data) { var input = $("<input hidden>"); input.attr("name", key); input.val(config.data[key]); $form.append(input); } $iframe.append($form); $(document.body).append($iframe); $form[0].submit(); $iframe.remove(); } //调用方法 downLoadFile({ url: '...', //请求的url data: { name:"", size:"", ...... }//要发送的数据 }); 使用以上方式即可使用post的方法下载文件 第二种方法很简单,可以先用post方式上传参数,将参数以缓存的形式存储在服务端并返回key给前端,再在post的回调方法中使用get方式传递key给后台下载文件 样例代码很简单,如下: $.post("download",params, function (data) { if (data.success) { window.location.href = "download?generatorId=" + data.data; } else { alert(data.msg); } })
|