本文转载于:http://www.blogjava.net/xcp/archive/2009/10/30/downloadlist.html
最近因为一个项目,需要做统一的下载,并且要支持批量下载..其中涉及到的知识点有:get请求中文处理,下载动态设置下载名,批量下载,动态打包,流处理,删除临时文件,使用迅雷下载后台发出两次次下载请求,以及struts2工作流程与原理等..
下面是我自己做的一个实例,主要实现遍历一个文件夹生成下载列表,用户可以单一下载,也可选择相关文件批量下载.....做的其中发现有很多疑惑的地方,请高手们指出....谢谢
一.实例区
1.index.html
======疑惑======
1.getDownloadFile()返回目标下载文件输入流跟struts2,然后struts2再生成输出流,对应struts.xml的<param name="inputName">downloadFile </param>
, 但是struts2后台不可能一次性将我们的输入流输出到输出流里面.. 而我们也就是不好控制,例在何时删除产生的临时文件,而且我上面删除临时文件的时候出错.(所有下面有一个struts2的工作流程,欢迎大家来讨论,指教,学习)
2.就下载的时候,如果用普通的window对话框形式来下载,一切正常.而我们用迅雷下载的时候,产生两个临时文件,当时把我雷惨了...后来打断点测试,确实迅雷下载的时候是重新发出了一次请求,虽然对下载无影响,但打包下载本身就比较慢,这样就对下载的性能有很大的影响,这也是我下面要问的问题
3.打包下载性能真的很差,有没有更好的批量下载方法,请大家指出..谢谢
最近因为一个项目,需要做统一的下载,并且要支持批量下载..其中涉及到的知识点有:get请求中文处理,下载动态设置下载名,批量下载,动态打包,流处理,删除临时文件,使用迅雷下载后台发出两次次下载请求,以及struts2工作流程与原理等..
下面是我自己做的一个实例,主要实现遍历一个文件夹生成下载列表,用户可以单一下载,也可选择相关文件批量下载.....做的其中发现有很多疑惑的地方,请高手们指出....谢谢
一.实例区
1.index.html
<%@ page language="java" pageEncoding="gbk"%> <% String path = request.getContextPath(); String basePath = request.getScheme()+"://"+request.getServerName()+":"+request.getServerPort()+path+"/"; %> <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"> <html> <head> <base href="<%=basePath%>"> <title>My JSP 'index.jsp' starting page</title> <meta http-equiv="pragma" content="no-cache"> <meta http-equiv="cache-control" content="no-cache"> <meta http-equiv="expires" content="0"> <meta http-equiv="keywords" content="keyword1,keyword2,keyword3"> <meta http-equiv="description" content="This is my page"> <!-- <link rel="stylesheet" type="text/css" href="styles.css"> --> </head> <body> <hr> <h3>欢迎光临下载区</h3> <a href="downloadList.action">下载列表</a><br/> </body> </html>2.配置struts.xml
<?xml version="1.0" encoding="UTF-8" ?> <!DOCTYPE struts PUBLIC "-//Apache Software Foundation//DTD Struts Configuration 2.0//EN" "http://struts.apache.org/dtds/struts-2.0.dtd"> <struts> <constant name="struts.custom.i18n.resources" value="message"></constant> <constant name="struts.i18n.encoding" value="gbk"></constant> <constant name="struts.multipart.saveDir" value="/tmp"></constant> <constant name="struts.multipart.maxSize" value="209715200" /> <package name="struts2" extends="struts-default"> <action name="downloadList" class="cn.edu.cuit.disasterSystem.web.struts2.action.DownloadListAction"> <result name="success">/downloadList.jsp</result> <result name="error">/downloadListError.jsp</result> </action> <action name="download" class="cn.edu.cuit.disasterSystem.web.struts2.action.DownloadAction"> <result name="success" type="stream"> <!-- contentType为二进制方式 --> <param name="contentType">application/octet-stream;charset=ISO8859-1</param> <!-- attachment属性强调是下载,就不会主动打开,比如图片 --> <!-- 使用经过转码的文件名作为下载文件名,downloadFileName属性对应action类中的方法 getDownloadFileName() --> <param name="contentDisposition"> attachment;filename=${filename} </param> <param name="inputName">downloadFile</param> <param name="bufferSize">4096</param> </result> <result name="input">/downloadList.jsp</result> <result name="error">/downloadListError.jsp</result> </action> </package> </struts>3.产生下载列表的Action----DownloadListAction
package cn.edu.cuit.disasterSystem.web.struts2.action; import java.io.File; import java.util.ArrayList; import java.util.HashMap; import java.util.Map; import org.apache.struts2.ServletActionContext; import com.opensymphony.xwork2.ActionContext; import com.opensymphony.xwork2.ActionSupport; /** * 显示所有down目录的文件,供下载所用 * @author xcp * @version 1.0 * Copyright (C), 2009 智能开发实验室 所有 * Program Name:灾情信息管理系统 * Date: 2009-10-24 上午11:16:41 */ @SuppressWarnings("serial") public class DownloadListAction extends ActionSupport{ private static ArrayList<String> filelist = new ArrayList<String>(); /** * 可以是前台一个页面传入,也可以是手动指定,其作用是指定下载文件的根目录 * @author 向才鹏 * 2009-10-24 下午12:02:47 */ private String downloadRootPath = "/upload"; public String getDownloadRootPath() { return downloadRootPath; } public void setDownloadRootPath(String downloadRootPath) { this.downloadRootPath = downloadRootPath; } /** * 将指定文件路径下的文件全部遍历出来 * @author 向才鹏 * @param strPath 指来要遍历的文件 * 2009-10-24 下午12:04:48 */ public static void refreshFileList(String strPath) { File dir = new File(strPath); File[] files = dir.listFiles(); if (files == null) return; for (int i = 0; i < files.length; i++) { if (files[i].isDirectory()) { refreshFileList(files[i].getAbsolutePath()); } else { String filePath = files[i].getPath(); filelist.add(filePath); } } } /** * 格式化输出数据存入Map,形式文件名+文件服务端路径 * @author 向才鹏 * @param filelist 遍历出来的文件路径 * @param downloadRootPath 指明服务器下载的文件,便于从遍历出来的文件中取得服务端路径 * @return * 2009-10-24 下午12:06:18 */ private static Map<String,String> formatFileMap(ArrayList<String> filelist,String downloadRootPath){ Map<String,String> formatFileMap = new HashMap<String,String>(); //得到服务下载的根路径,并将/换成\\,这样便于替换 String formatDownloadRootPath = downloadRootPath.replaceAll("/", "\\\\"); for(String filePath : filelist){ //得到下载的相对路径 String downloadPath = filePath.substring(filePath.indexOf(formatDownloadRootPath)); //将得到的相对路径的\\转换成/ String formatDownloadPath = downloadPath.replaceAll("\\\\", "/"); //得到文件名 String filename = formatDownloadPath.substring(formatDownloadPath.lastIndexOf("/")+1); /*try { formatFileMap.put(filename, URLEncoder.encode(formatDownloadPath, "gbk")); } catch (UnsupportedEncodingException e) { formatFileMap.put(filename, formatDownloadPath); e.printStackTrace(); }*/ //这就不用考虑设置编码了,再后面统一使用javascript的encodeURI函数 formatFileMap.put(filename, formatDownloadPath); } return formatFileMap; } @SuppressWarnings("unchecked") @Override public String execute() throws Exception { //指定下载目录 String upload = ServletActionContext.getServletContext().getRealPath(downloadRootPath); //清理filelist filelist.clear(); //遍历文件 refreshFileList(upload); ActionContext context = ActionContext.getContext(); Map request = (Map) context.get("request"); if(filelist != null){ //格式化文件信息,包括文件名和地址 Map<String,String> formatFileMap = formatFileMap(filelist,downloadRootPath); request.put("fileMap", formatFileMap); return SUCCESS; } else{ request.put("errorMessage", "没有相关的下载文件"); return ERROR; } } }4.显示下载列表downloadList.jsp
<%@ page language="java" contentType="text/html; charset=gbk" pageEncoding="gbk"%> <%@ taglib prefix="s" uri="/struts-tags"%> <!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd"> <script type="text/javascript"> function downloadFile1(filenames,filepaths){ location.href=encodeURI("download.action?filenames="+filenames+"&filepaths="+filepaths); } function SelectAll(oForm) { for(var i=0;i<oForm.url.length;i++) { oForm.url[i].checked=true; } } function TurnOver(oForm) { for(var i=0;i<oForm.url.length;i++) { oForm.url[i].checked=!oForm.url[i].checked; } } function DownlodSelected(oForm){ if(confirm("因需要在服务端动态打包,需要时间比较长,是否继续批量下载?")) { var arrDownloadList = []; for(var i=0;i<oForm.url.length;i++){ if(oForm.url[i].checked==true){ if(arrDownloadList.length==0){ arrDownloadList[0] = oForm.url.value; } arrDownloadList[arrDownloadList.length] = oForm.url[i].value; } } if (arrDownloadList.length>0){ var temp = []; var filenames=""; var filepaths=""; for(var i=1;i<arrDownloadList.length;i++){ temp = arrDownloadList[i].split(",") if(filenames=="" && filepaths==""){ filenames=temp[0] filepaths=temp[1] }else{ filenames=filenames+"|"+temp[0]; filepaths=filepaths+"|"+temp[1]; } } downloadFile1(filenames,filepaths); }else{ alert("还没有选中下载项"); } } } </script> <html> <head> <meta http-equiv="Content-Type" content="text/html; charset=GB18030"> <title>Insert title here</title> <script type="text/javascript" src="dwr/engine.js"></script> <script type="text/javascript" src="dwr/util.js"></script> <script type="text/javascript" src="dwr/interface/downloaddwr.js"></script> </head> <body> <form name="myform" style="display: inline" onSubmit="return false"> <table width="50%" align="center"> <tr> <td colspan="2"> <h3> 以后是下载列表,点击进行下载 </h3> </td> </tr> <tr> <td colspan="2"> <font color="red"><s:fielderror></s:fielderror> </font> </td> </tr> <s:iterator value="#request.fileMap" status="stuts"> <s:if test="#stuts.odd == true"> <tr style="background-color: #77D9F6"> <td> <input name="url" type="checkbox" id="url" value="<s:property value="key" />,<s:property value="value" />"> </td> <td> <s:property value="key" /> </td> <td> <a href="#" onclick="downloadFile1('<s:property value="key" />','<s:property value="value" />')">点击下载</a> </td> </tr> </s:if> <s:else> <tr style="background-color: #D7F2F4"> <td> <input name="url" type="checkbox" id="url" value="<s:property value="key" />,<s:property value="value" />"> </td> <td> <s:property value="key" /> </td> <td> <a href="#" onclick="downloadFile1('<s:property value="key" />','<s:property value="value" />')">点击下载</a> </td> </tr> </s:else> </s:iterator> </table> <div align="center"> <input class="green_at_bn" title="选择下载的文件" onClick="SelectAll(this.form)" type="button" value="全选"> <input class="green_at_bn" title="反向选择下载文件" onClick="TurnOver(this.form)" type="button" value="反选"> <input class="green_at_bn" title="下载选中文件" onClick="DownlodSelected(this.form)" type="button" value="批量下载文件"> </div> </form> <frame src="" id="dis"> </frame> </body> </html>5.统一处理下载的Action----DownloadAction
package cn.edu.cuit.disasterSystem.web.struts2.action; import java.io.File; import java.io.FileInputStream; import java.io.FileOutputStream; import java.io.IOException; import java.io.InputStream; import java.io.UnsupportedEncodingException; import java.text.SimpleDateFormat; import java.util.Date; import org.apache.struts2.ServletActionContext; import org.apache.tools.zip.ZipEntry; import org.apache.tools.zip.ZipOutputStream; import com.opensymphony.xwork2.ActionSupport; /** * 统一下载类 * * @author xcp * @version 1.0 Copyright (C), 2009 智能开发实验室 所有 Program Name:灾情信息管理系统 * Date: 2009-10-30 上午09:06:01 */ @SuppressWarnings("serial") public class DownloadAction extends ActionSupport { private String filenames; private String filepaths; private String[] filenameArray = null; private String[] filepathArray = null; private String filename; private String filepath; private SimpleDateFormat format = new SimpleDateFormat("yyyyMMddHHmmss"); /** * 得到客户端请求的文件名字符串 * @author 向才鹏 * @return 客户端请求的文件名字符串 * 2009-10-30 下午11:21:31 */ public String getFilenames() { return filenames; } /** * 将客户端请求的文件名字符串set到filenames变量 * @author 向才鹏 * @param filenames * 2009-10-30 下午11:21:34 */ public void setFilenames(String filenames) { this.filenames = filenames; if (this.filenames.contains("|")) { parseFilenamesToArray(); } } /** * 得到客户端请求的文件路径字符串 * @author 向才鹏 * @return 客户端请求的文件路径字符串 * 2009-10-30 下午11:21:37 */ public String getFilepaths() { return filepaths; } /** * 将客户端请求的文件路径字符串set到filepaths变量 * @author 向才鹏 * @param filepaths * 2009-10-30 下午11:21:40 */ public void setFilepaths(String filepaths) { this.filepaths = filepaths; if (this.filepaths.contains("|")) { parseFilepathsToArray(); } } /** * 解析客户端请求下载的文件名 * @author 向才鹏 * 2009-10-30 下午11:23:43 */ public void parseFilenamesToArray() { filenameArray = filenames.split("\\|"); } /** * 解析客户端请求下载的文件路径 * @author 向才鹏 * 2009-10-30 下午11:23:46 */ public void parseFilepathsToArray() { filepathArray = filepaths.split("\\|"); } /** * 得到下载显示名,对就struts.xml配置文件<param name="contentDisposition">attachment;filename=${filename}</param> * 要想正确的显示中文文件名,我们需要对fileName再次编码 否则中文名文件将出现乱码,或无法下载的情况 * @author 向才鹏 * @return 返回下载显示名 * 2009-10-30 下午11:26:49 */ public String getFilename() { try { return new String(filename.getBytes(), "ISO-8859-1"); } catch (UnsupportedEncodingException e) { e.printStackTrace(); return filename; } } /** * 得到下载文件路径 * @author 向才鹏 * @return 返回下载路径 * 2009-10-30 下午11:27:52 */ public String getFilepath(){ return filepath; } /** * 初始化下载文件名 * @author 向才鹏 * 2009-10-30 下午11:29:00 */ public void initFilename() { if(isBaleZip()){ this.filename = "批量打包下载.zip"; }else{ this.filename = getFilenames(); } System.out.println("下载文件名: "+filename); } /** * 初始化下载路径 * @author 向才鹏 * 2009-10-30 下午11:30:04 */ public void initFilepath() { if(isBaleZip()){ String rootpath = ServletActionContext.getServletContext().getRealPath("/upload/temp"); String requestip = ServletActionContext.getRequest().getLocalAddr(); //this.filepath = "c:\\批量打包下载.zip"; this.filepath = rootpath+"\\"+requestip+"-"+format.format(new Date())+".zip"; }else{ this.filepath = getFilepaths(); } System.out.println("下载文件路径: "+filepath); } /** * 判断是否符合打包要求 * @author 向才鹏 * @return 否符合打包要求 * 2009-10-30 上午11:36:09 */ public boolean isBaleZip(){ boolean isZip = false; if(this.filenameArray!= null && this.filepathArray!= null && this.filenameArray.length>0 && this.filenameArray.length==this.filepathArray.length){ isZip = true; } return isZip; } /** * 压缩文件 * @author 向才鹏 * @param zipFilePath 产生的压缩文件路径和名字 * @param names 传入要进行打包的所有文件名 * @param paths 传入要进行打包的所有文件路径 * @throws IOException * 2009-10-30 下午11:39:14 */ public void baleZip(String zipFilePath,String[] names,String[] paths) throws IOException{ File f = new File(zipFilePath); f.createNewFile(); ZipOutputStream out = new ZipOutputStream(new FileOutputStream(f)); out.putNextEntry(new ZipEntry("/")); for(int i=0;i<paths.length;i++){ out.putNextEntry(new ZipEntry(names[i])); InputStream in =ServletActionContext.getServletContext().getResourceAsStream(paths[i]); int b; while ((b = in.read()) != -1) { out.write(b); } in.close(); } out.flush(); out.close(); } /** * 返回目标下载文件输入流跟struts2,然后struts2再生成输出流,对应struts.xml的<param name="inputName">downloadFile </param> * 但是struts2后台不可能一次性将我们的输入流输出到输出流里面.. 而我们也就是不好控制,例在何时删除产生的临时文件 * @author 向才鹏 * @return 目标下载文件输入流 * 2009-10-30 上午11:45:29 */ public InputStream getDownloadFile(){ initFilename(); initFilepath(); InputStream in = null; File tempfile = null; if(isBaleZip()){ try { baleZip(this.filepath,this.filenameArray,this.filepathArray); tempfile = new File(this.filepath); in = new FileInputStream(tempfile); } catch (IOException e) { System.out.println(e.getMessage()+" "+"压缩文件出错!!"); return null; } finally{ if(tempfile.exists()){ tempfile.delete(); if(tempfile.exists()){ System.out.println("------删除临时文件失败-------"); }else{ System.out.println("------删除打包产生的临时文件------"); } } } }else{ in = ServletActionContext.getServletContext().getResourceAsStream(getFilepath()); } return in; } /** * 而这种文件下载方式却是存在安全隐患的, 因为访问者如果精通Struts2的话,它可能使用这样的带有表单参数的地址来访问: * http://localhost:8080/disasterSystem/download.action?filename=%E6%B5%8B%E8%AF%95%E4%B8%8B%E8%BD%BD&filepath=/WEB-INF/web.xml * 这样的结果就是下载后的文件内容是您系统里面的web.xml的文件的源代码,甚至还可以用这种方式来下载任何其它JSP文件的源码, 这对系统安全是个很大的威胁。 * 作为一种变通的方法,读者最好是从数据库中进行路径配置,然后把Action类中的设置inputPath的方法统统去掉,简言之就是所有set方法定义 * 第二种方法,读者可以在execute()方法中进行路径检查,如果发现有访问不属于download下面文件的代码,就一律拒绝,不给他们返回文件内容。 * * @author 向才鹏 * @param filepath * 2009-10-30 上午09:34:43 */ @Override public String execute() throws Exception { // 文件下载目录路径 String downloadDir = "/upload"; // 发现企图下载不在 /download 下的文件, 就显示空内容 if (!filepaths.startsWith(downloadDir)) { // 可以抛出一些异常信息 System.out.println("只能下载upload里面的东西,谢谢!"); return ERROR; } return SUCCESS; } }
======疑惑======
1.getDownloadFile()返回目标下载文件输入流跟struts2,然后struts2再生成输出流,对应struts.xml的<param name="inputName">downloadFile </param>
, 但是struts2后台不可能一次性将我们的输入流输出到输出流里面.. 而我们也就是不好控制,例在何时删除产生的临时文件,而且我上面删除临时文件的时候出错.(所有下面有一个struts2的工作流程,欢迎大家来讨论,指教,学习)
2.就下载的时候,如果用普通的window对话框形式来下载,一切正常.而我们用迅雷下载的时候,产生两个临时文件,当时把我雷惨了...后来打断点测试,确实迅雷下载的时候是重新发出了一次请求,虽然对下载无影响,但打包下载本身就比较慢,这样就对下载的性能有很大的影响,这也是我下面要问的问题
3.打包下载性能真的很差,有没有更好的批量下载方法,请大家指出..谢谢
相关推荐
在Struts2框架中,批量文件下载是一种常见的...综上所述,Struts2的批量文件下载涉及前端页面设计、Struts2配置、后台下载逻辑以及错误处理等多个方面。通过合理的设计和实现,可以提供高效、安全的批量文件下载功能。
综上所述,Struts2框架下的批量下载功能不仅涉及到注解的灵活运用,还需要对文件流、压缩技术、临时文件管理、日期时间处理、字符编码以及日志记录等多方面知识有深刻理解。同时,合理设计实体类与服务层的交互,...
在Struts2中处理文件下载和压缩功能是常见的需求,这通常涉及到用户请求从服务器获取文件或者打包多个文件为一个ZIP压缩包。下面将详细介绍这两个知识点。 **文件下载** 在Struts2中,文件下载可以通过Action类来...
标题 "SWFupload_struts1.rar_java 批量上传_struts1批量下载_swfupload struts1" 提供的信息表明,这是一个与Java编程语言、Struts1框架以及SWFUpload工具相关的项目或教程。SWFUpload是一个流行的老牌JavaScript和...
2.实现了文件在线压缩解压功能(可以压缩成zip格式,可以直接解压缩RAR文件格式) 3.实现了新建文件夹,以及删除文件和文件夹。 基于Struts2和Spring的网络硬盘系统,批量上传文件和在线解压,优秀源代码! 基于Struts...
然而,随着时间的推移,Struts2框架发现了一系列的安全漏洞,这些漏洞可能导致远程代码执行、敏感信息泄露等问题,对使用Struts2的系统构成了严重的安全威胁。本资源提供的“Struts2漏洞检测(带自己编写使用说明一...
5. **示例应用**:如果提供的压缩包包含一个完整的示例应用,那么你可以直接运行这个应用,了解如何在实际项目中使用JCaptcha4Struts2插件。这可能包括Web应用的部署描述符(如`web.xml`),以及必要的Maven或Gradle...
在【压缩包子文件的文件名称列表】中,"SWFuploadForstruts1"可能是一个包含SWFupload整合Struts1框架的示例代码或者配置文件的压缩包。Struts1是一个经典的Java Web MVC框架,用于构建企业级应用程序。将SWFupload...
基本架构 Struts2 + Spring2 + ExtJS2.2 无数据库 实现了多文件批量队列上传,有完美进度条列表,已经详细的上传信息显示. 实现了多文件压缩,以及解压缩文件,包括解压缩rar文件 实现了文件下载.等基本功能 此项目继续...
这意味着开发者可能针对Struts2原有的数据操作接口进行了增强,例如添加了批量更新、事务管理、错误处理等特性,使得在处理数据库操作时更加高效和安全。同时,由于框架“还在测试与开发”阶段,这表明它可能包含...
根据【压缩包子文件的文件名称列表】"mySSM-master",我们可以推测压缩包内包含了项目的所有源代码文件、配置文件、测试文件以及可能的资源文件,如图片、CSS样式表和JavaScript脚本。具体的文件结构可能如下: - `...
在IT行业中,图像上传处理是一项常见的功能,尤其在Web应用中。本项目主要涉及了Struts2、Servlet和ExtJS这三个技术,它们是构建...通过优化和扩展,该系统可以适应更多复杂的需求,如批量上传、图片压缩、水印添加等。
此外,前端也可以通过压缩图片、调整分辨率等手段减小文件大小。 8. **错误处理**:良好的错误处理机制是必不可少的。无论是前端的文件选择错误,还是后端的上传失败,都需要向用户清晰反馈,提供重试或取消选项。 ...
- 功能上,系统需支持用户注册登录、文件目录清晰展示、批量上传下载、文件预览、打包压缩下载以及外链设置等功能。操作应简便快捷,以优化用户体验。 - 性能上,强调高运行速度、快速响应和处理能力,以及足够的...
在【压缩包子文件的文件名称列表】中,"99273887dbboard_struts2"可能指的是系统的一部分或某个模块,比如后台管理界面或者使用Struts2框架的部分。Struts2是Java Web开发中常用的MVC框架,它负责控制应用程序的流程...
5. **文件预处理**:在上传前,可以使用SWFUpload提供的API对选中的文件进行预处理,比如重命名、压缩或者验证文件内容。 6. **UI自定义**:虽然SWFUpload默认提供了一些基本的按钮样式,但开发者可以根据需要...
- SpringMVC基于前端控制器模式,而Struts2基于拦截器模式。 - SpringMVC更加灵活,易于与其他Spring组件集成。 - Struts2提供了更多的内置标签库和插件支持。 - **SpringIOC和AOP的概念以及实现方式**: - **...
10. **性能优化**:DWR 3.0版本对性能进行了优化,例如,通过批量调用来减少网络请求次数,以及使用GZIP压缩来减小传输数据量。 使用DWR 3.0版本,开发者可以构建出更富交互性和响应性的Web应用,同时避免了传统...
- 论坛和社区:参与DWR相关的论坛讨论,可以解决学习过程中的问题,并获取最新的开发信息。 7. **实践与进阶**: - 与框架集成:DWR可以与Spring、Struts等常见的Java Web框架结合使用,增强框架的功能。 - 性能...