您的位置:首页 > 编程语言 > Java开发

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

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

四.讨论struts2流程

1.我加载struts2的FilterDispatcher类的init()方法处打下断点,可以明显看出从tomcat到struts2工作的整个流
程,大家都看看,把学到的跟小弟共享下.

2. 一个傻傻的问题,但是要真正把它弄清楚也不容易,Servlet,Filter,Intercept,Struts2工作底层到底有何
联系..

请高手多多指教!!!!
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: