strtus2 批量下载 中文问题、压缩文件等 ------ 讨论struts2工作流程

最近因为一个项目,需要做统一的下载,并且要支持批量下载..其中涉及到的知识点有: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 >
           
< 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 >
                                
< 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 >
                                
< 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.打包下载性能真的很差,有没有更好的批量下载方法,请大家指出..谢谢




      四.讨论struts2流程
      1.我加载struts2的FilterDispatcher类的init()方法处打下断点,可以明显看出从tomcat到struts2工作的整个流 程,大家都看看,把学到的跟小弟共享下.
      2. 一个傻傻的问题,但是要真正把它弄清楚也不容易,Servlet,Filter,Intercept,Struts2工作底层到底有何 联系..

      请高手多多指教!!!!

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值