MyException - 我的异常网
当前位置:我的异常网» 软件架构设计 » strtus2 批量上载 中文有关问题、压缩文件等 - 讨论

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

www.myexceptions.net  网友分享于:2015-08-26  浏览:5次
strtus2 批量下载 中文问题、压缩文件等 ------ 讨论struts2工作流程
本文转载于:http://www.blogjava.net/xcp/archive/2009/10/30/downloadlist.html

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

文章评论

如何成为一名黑客
如何成为一名黑客
十大编程算法助程序员走上高手之路
十大编程算法助程序员走上高手之路
编程语言是女人
编程语言是女人
程序员眼里IE浏览器是什么样的
程序员眼里IE浏览器是什么样的
10个调试和排错的小建议
10个调试和排错的小建议
老美怎么看待阿里赴美上市
老美怎么看待阿里赴美上市
初级 vs 高级开发者 哪个性价比更高?
初级 vs 高级开发者 哪个性价比更高?
“肮脏的”IT工作排行榜
“肮脏的”IT工作排行榜
5款最佳正则表达式编辑调试器
5款最佳正则表达式编辑调试器
“懒”出效率是程序员的美德
“懒”出效率是程序员的美德
每天工作4小时的程序员
每天工作4小时的程序员
我是如何打败拖延症的
我是如何打败拖延症的
程序员必看的十大电影
程序员必看的十大电影
一个程序员的时间管理
一个程序员的时间管理
那些性感的让人尖叫的程序员
那些性感的让人尖叫的程序员
程序员的鄙视链
程序员的鄙视链
60个开发者不容错过的免费资源库
60个开发者不容错过的免费资源库
为啥Android手机总会越用越慢?
为啥Android手机总会越用越慢?
我跳槽是因为他们的显示器更大
我跳槽是因为他们的显示器更大
旅行,写作,编程
旅行,写作,编程
程序员周末都喜欢做什么?
程序员周末都喜欢做什么?
什么才是优秀的用户界面设计
什么才是优秀的用户界面设计
老程序员的下场
老程序员的下场
团队中“技术大拿”并非越多越好
团队中“技术大拿”并非越多越好
程序员最害怕的5件事 你中招了吗?
程序员最害怕的5件事 你中招了吗?
Java 与 .NET 的平台发展之争
Java 与 .NET 的平台发展之争
2013年美国开发者薪资调查报告
2013年美国开发者薪资调查报告
看13位CEO、创始人和高管如何提高工作效率
看13位CEO、创始人和高管如何提高工作效率
Web开发者需具备的8个好习惯
Web开发者需具备的8个好习惯
总结2014中国互联网十大段子
总结2014中国互联网十大段子
Java程序员必看电影
Java程序员必看电影
那些争议最大的编程观点
那些争议最大的编程观点
Web开发人员为什么越来越懒了?
Web开发人员为什么越来越懒了?
要嫁就嫁程序猿—钱多话少死的早
要嫁就嫁程序猿—钱多话少死的早
Google伦敦新总部 犹如星级庄园
Google伦敦新总部 犹如星级庄园
程序猿的崛起——Growth Hacker
程序猿的崛起——Growth Hacker
程序员都该阅读的书
程序员都该阅读的书
10个帮程序员减压放松的网站
10个帮程序员减压放松的网站
不懂技术不要对懂技术的人说这很容易实现
不懂技术不要对懂技术的人说这很容易实现
聊聊HTTPS和SSL/TLS协议
聊聊HTTPS和SSL/TLS协议
科技史上最臭名昭著的13大罪犯
科技史上最臭名昭著的13大罪犯
为什么程序员都是夜猫子
为什么程序员都是夜猫子
代码女神横空出世
代码女神横空出世
我的丈夫是个程序员
我的丈夫是个程序员
漫画:程序员的工作
漫画:程序员的工作
中美印日四国程序员比较
中美印日四国程序员比较
亲爱的项目经理,我恨你
亲爱的项目经理,我恨你
程序员的一天:一寸光阴一寸金
程序员的一天:一寸光阴一寸金
2013年中国软件开发者薪资调查报告
2013年中国软件开发者薪资调查报告
软件开发程序错误异常ExceptionCopyright © 2009-2015 MyException 版权所有