STRUTS2文件上传和下载
Struts2文件上传和下载
1.???? 文件上传的原理:
表 单元素的enctype属性指定的是表单数据的编码方式,该属性有3个值:
1)????? application/x-www-form-urlencoded:这是默认编码方式,它只处理表单域里的value属性值,采用这种编码方式的表单 会将表单域的值处理成URL编码方式。
2)????? multipart/form-data:这种编码方式的表单会以二进制流的方式来处理表单数据,这种编码方式会把文件域指定文件的内容也封装到请求参数 里。
3)????? text/plain:这种方式主要适用于直接通过表单发送邮件的方式。
文件上传是web应用经常用到的一个知识。 原理是,通过为表单元素设置enctype="multipart/form-data"属性,让表单提交的数据以二进制编码的方式提交,在接收此请求的 Servlet中用二进制流来获取内容,就可以取得上传文件的内容,从而实现文件的上传。
在Java领域中,有两个常用的文件上传项目:一个是 Apache组织Jakarta的Common-FileUpload组件(http://commons.apache.org/fileupload/ ), 另一个是Oreilly组织的COS框架(http://www.servlets.com/cos/ )。 利用这两个框架都能很方便的实现文件的上传。
2.???? Struts2 的 文件上传:
Struts2并未提供自己的请求解析器,也就是就Struts2不会自己去处理multipart/form- data的请求,它需要调用其他请求解析器,将HTTP请求中的表单域解析出来。但Struts2在原有的上传解析器基础上做了进一步封装,更进一步简化 了文件上传。
Struts2默认使用的是Jakarta的Common-FileUpload框架来上传文件,因此,要在web应用中增加两个 Jar文件:commons-fileupload-1.2.jar和commons-io-1.3.1.jar。它在原上传框架上做了进一步封装,简化 了文件上传的代码实现,取消了不同上传框架上的编程差异。
如果要改成其它的文件上传框架,可以修改 struts.multipart.parser常量的值为cos/pell,默认值是jakata。并在classpath中增加相应上传组件的类库。
2.1.???? 步骤一:创建带上传表单域的页面
<%@ page language = "java" contentType = "text/html; charset=UTF-8" %>
< html >
< head >
??? < title > Struts2 File Upload </ title >
</ head >
< body >
??? < form action = "fileUpload.action" method = "POST" enctype = "multipart/form-data" >
??????? 文件标题: < input type = "text" name = "title" size = "50" />< br />
??????? 选择文件: < input type = "file" name = "upload" size = "50" />< br />
?????? < input type = "submit" value = " 上传 " />????????
??? </ form >
</ body >
</ html >
此页面特殊之处只是把表单的enctype属性设置为multipart/form-data。
2.2.???? 步骤二:创建处理上传请求的 Action 类
package org.qiujy.web.struts2;
import java.io.BufferedInputStream;
import java.io.BufferedOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.InputStream;
import java.io.OutputStream;
import org.apache.struts2.ServletActionContext;
import com.opensymphony.xwork2.ActionSupport;
/**
? * 处理文件上传的 Action 类
? * @author qiujy
? * @version 1.0
? */
public class FileUploadAction extends ActionSupport {
??? private static final int BUFFER_SIZE = 16 * 1024;
??? // 文件标题
??? private String title ;
??? // 上传文件域对象
??? private File upload ;
??? // 上传文件名
??? private String uploadFileName ;
??? // 上传文件类型
??? private String uploadContentType ;
??? // 保 存文件的目录路径 ( 通过依赖注入 )
??? private String savePath ;
??? // 以下省略 getter 和 setter......
??? // 自己封装的一个把源文件对象 复制成目标文件对象
??? private static void copy(File src, File dst) {
??????? InputStream in = null ;
??????? OutputStream out = null ;
??????? try {
??????????? in = new BufferedInputStream( new FileInputStream(src), BUFFER_SIZE );
??????????? out = new BufferedOutputStream( new FileOutputStream(dst),
??????????????????? BUFFER_SIZE );
??????????? byte [] buffer = new byte [ BUFFER_SIZE ];
??????????? int len = 0;
??????????? while ((len = in.read(buffer)) > 0) {
??????????????? out.write(buffer, 0, len);
??????????? }
??????? } catch (Exception e) {
??????????? e.printStackTrace();
??????? } finally {
??????????? if ( null != in) {
??????????????? try {
??????????????????? in.close();
??????????????? } catch (IOException e) {
??????????????????? e.printStackTrace();
??????????????? }
??????????? }
??????????? if ( null != out) {
??????????????? try {
??????????????????? out.close();
??????????????? } catch (IOException e) {
??????????????????? e.printStackTrace();
??????????????? }
??????????? }
??????? }
??? }
??? @Override
??? public String execute() throws Exception {
??????? // 根据服务器的文件保存地址和 原文件名创建目录文件全路径
??????? String dstPath = ServletActionContext. getServletContext ()
??????????????????????????????? .getRealPath( this .getSavePath())
??????????????????????????????? + "
" + this .getUploadFileName();
???????
??????? System. out .println( " 上传的文件的类型: " + this .getUploadContentType());
???????
??????? File dstFile = new File(dstPath);
??????? copy ( this . upload , dstFile);
??????? return SUCCESS ;
??? }
{color:black}} |
上面这个Action类中,提供了title和upload两个 属性来分别对应页面的两个表单域属性,用来封装表单域的请求参数。
但是,值得注意的是,此Action中还有两个属 性:uploadFileName和uploadContentType,这两个属性分别用于封装上传文件的文件名、文件类型。这是Struts2设计的 独到之处:Strut2的Action类直接通过File类型属性直接封装了上传文件的文件内容,但这个File属性无法获取上传文件的文件名和文件类 型,所以Struts2就直接将文件域中包含的上传文件名和文件类型的信息封装到uploadFileName和uploadContentType属性 中,也就是说Struts2 针对表单中名为 xxx 的 文件域,在对应的 Action 类中使用 3 个 属性来封装该文件域信息 :
l 类型为File的xxx属性:用来封装页面文件域对应的文件内容。
l 类型为 String的xxxFileName属性:用来封装该文件域对应的文件的文件名。
l 类型为String的xxxContentType属 性:用来封装该文件域应用的文件的文件类型。
另外,在这个Action类中还有一个savePath属性,它的值是通过配置文件来动态设置的, 这也是Strut2设计中的一个依赖注入特性的使用。
2.3.???? 步 骤三:配置
struts.xml文件:
| <!DOCTYPE struts PUBLIC
??????? "-//Apache Software Foundation//DTD Struts Configuration 2.0//EN"
??????? "http://struts.apache.org/dtds/struts-2.0.dtd" >
< struts >
??? < package name = "fileUploadDemo" extends = "struts-default" >
??????? < action name = "fileUpload"
class = "org.qiujy.web.struts2.FileUploadAction" >
<!-- 动 态设置 Action 中的 savePath 属性的值 -->
??????????? < param name = "savePath" > /upload </ param >
??????????? < result name = "success" > /showupload.jsp </ result >
??????? </ action >
??? </ package >
</ struts >
?????? 在这个文件中跟以前配置唯一不同的是给action配置了一个<param .../>元素,用来为该Action的savePath属性动态注入值。
web.xml中的配置跟以前的应用一样。说明一点:好多网络文 章说Struts2上传时要在web.xml中配置一个名为ActionContextUp的过滤器,说是有一些莫名的错误,可是是我用了Struts2 新版本2.0.9GA版,测了n次,没出现什么问题,所以没配置。
2.4.???? 运行调试:
运行前要在根目录下创建一个名为upload的文件夹,用来存放上传后的文件。
上传结 果:
3.???? 文件类型及错误输出:
Struts2 提供了一个文件上传的拦截器(名为fileUpload ), 通过配置这个拦截器能轻松地实现文件类型的过滤。
在上例中,若要配置上传的文件只能是一些普通的图片文件格式:image/bmp、image /png、image/gif、image/jpeg、image/jpg等,则可在struts.xml文件中按如下方式配置:
<! 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 = "messages" />
??? < package name = "fileUploadDemo" extends = "struts-default" >
??????? < action name = "fileUpload"
??????????? class = "org.qiujy.web.struts2.FileUploadAction" >
??????????? < interceptor-ref name = "fileUpload" >
????????????? <!-- 配置允许上传的文件类型,多个用 "," 分隔 -->
????????????? < param name = "allowedTypes" >
*??? *image/bmp,image/png,image/gif,image/jpeg,image/jpg ,image/x-png, image/pjpeg
????????????? </ param >
????????????? <!-- 配置允许上传的文件大小,单位字节 -->
????????????? < param name = "maximumSize" > 102400 </ param >
?????????? </ interceptor-ref >
?????????? < interceptor-ref name = "defaultStack" />
??????????? <!-- 动 态设置 Action 中的 savePath 属性的值 -->
??????????? < param name = "savePath" > /upload </ param >
??????????? < result name = "input" > /index.jsp </ result >
??????????? < result name = "success" > /showupload.jsp </ result >
??????? </ action >
??? </ package >
</ struts >
如果上传文件失败,系统返回到input对应的页面,要在input对应的页面输出文件过滤失败信息,可以在input对应的页面中增加 <s:fielderror/>来显示错误信息。
运行调试:
结果:
???????? 显然,这样的提示不太友好,应用使用国际化信息。在国际化资源文件中添加如下三句:
# 更改上传文件类型不允许的提示信息
struts.messages.error.content.type.not.allowed= 文件上传失败:你要上传的文件类型不允许
# 更改上传文件太大的提示信息
struts.messages.error.file.too.large= 文件上传失败:你要上传的文件太大
# 文件上传其它错误信息
struts.messages.error.uploading= 文件上传失败:发生内部错误
以上是我在 网上找的一个比较详细的Struts2文件上传的解说但在实际中还要注意一些问题
1. 如果用到了spring的方法注入的话一定不要忘了加 scope="prototype"这样每次访问action时,都会新建一个action,否则的话,会记录上传action留下的记录,出现意想不到 的错误。
2. 上传时出现的错误信息是放在request范围内的如果出现错误返回的input页面不是上传文件的form页面那么可以把错误信息提取出来
String error = this.getFieldErrors().get("file").toString();(file指struts2 file标签里 name的名字)
放 在request,session。。范围内然后在input指定的页面显示错误信息。
3. 文件大小和类型的限制是通过Struts2拦截器实现的你只需要在上传的action中做如下配置就可以
<param name="allowedTypes">
????? application/octet-stream,application/x-zip-compressed,image/bmp,image/png,image/gif,image/jpeg,image/jpg,image/x-png,
????? image/pjpeg
</param>
<param name="maximumSize">5242880</param>
但 是有一点要注意Struts2默认文件上传最大为2M即便你设置了<param name="maximumSize">5242880</param>
当 上传的文件大于2M时候也会出错的这时要设置另外一个常量<constant name="struts.multipart.maxSize" value="1000000000"/>
要 让他的value设置的比你限定上传最大值要大一点。
以 上是文件上传要注意的几点其实文件上传看起来比较难实际上比较简单,而文件下载就有点麻烦了遇到了很恼人的编码和乱码问题。
1. 文件下载的action和普通的action查不多仅仅是该Action需要提供一个返回InputStream流的方法
public InputStream getInputStream() throws Exception
{??
? Attachment attachment=attachmentManager.get(Long.valueOf(attachmentId));??
? File downFile=new File(attachment.getFilePath());?? InputStream inStream = new FileInputStream(downFile);??
? return inStream;?
}
有 一点要注意struts2提供了一个 ServletActionContext.getServletContext().getResourceAsStream(filePath);方 法根据路径返回输入流,但是这个方法和servletContext有关,当你把下载的文件放在项目的根目录中才可以否则会出错的,最保险的办法就是根据 文件路径找到这个文件然后在转化成输入流返回。
配 置:
<action name="download"
?? method="downloadAttachment">
?? <result name="success" type="stream">
??? <param name="bufferSize">2048</param> //输入流缓冲的大小
??? <param name="inputPath">inputStream</param>? //inputStream和 getInputStream() 是对应的
?? </result>
? </action>
2. 编码,当文件名是中文时
String downFileName = new String(downFileName.getBytes("ISO8859-1"), "utf-8");
getResponse().setHeader(
??? "Content-Disposition",
??? "attachment; filename="
????? + java.net.URLEncoder.encode(downFileName, "UTF-8"));
setHeader 是设置http请求的头是MIME 协议的拓展可以实现以下功能
* 希望某类或者某已知MIME 类型的文件(比如:.gif; .txt;*.htm)能够在访问时弹出"文件下载"对话 框
*希望以原始文件名(上传时的文件名,例如:山东省政府1024号文件.doc)提供下载,但服务器上保存的地址却是其他文件名 (如:12519810948091234_asdf.doc)
*希望某文件直接在浏览器上显示而不是弹出文件下载对话框 上面的代码实现了第一个功能。
最近因为一个项目,需要做统一的下载,并且要支持批量下载..其中涉及到的知识点有: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.get请求中文处理参见:http://www.blogjava.net/xcp/archive/2009 /10/29/download2.html
?????? 2.文件打包参见:http://www.blogjava.net/xcp/archive/2009/10/30 /CompressToZip.html
????
????? 三.本人疑惑区
????? 1.getDownloadFile()返回目标下载文件输入流跟struts2,然后struts2再生成输出流,对应struts.xml 的<param name="inputName">downloadFile </param>
, 但是struts2后台不可能一次性将我们的输入流输出到输出流里面.. 而我们也就是不好控制,例在何时删除产生的临时文件,而且我上面删除临时文件的时候出错.(所有下面有一个struts2的工作流程,欢迎大家来讨论,指 教,学习)
???? 2.就下载的时候,如果用普通的window对话框形式来下载,一切正常.而我们用迅雷下载的时候,产生两个临时文件,当时把我雷惨了...后来打断点测 试,确实迅雷下载的时候是重新发出了一次请求,虽然对下载无影响,但打包下载本身就比较慢,这样就对下载的性能有很大的影响,这也是我下面要问的问题
???? 3.打包下载性能真的很差,有没有更好的批量下载方法,请大家指出..谢谢