- 浏览: 164434 次
- 性别:
- 来自: 上海
文章分类
最新评论
-
钱少少:
programming 写道和maven相比没优势
ivy只是 ...
ant+ivy管理项目 -
programming:
和maven相比没优势
ant+ivy管理项目 -
钱少少:
kingsfighter 写道和maven相比,有什么优势?
...
ant+ivy管理项目 -
kingsfighter:
和maven相比,有什么优势?
ant+ivy管理项目 -
zhijie_zhang:
搭建java-web框架(一)
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" class="attachmentAction"
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.打包下载性能真的很差,有没有更好的批量下载方法,请大家指出..谢谢
四.讨论struts2流程
1.我加载struts2的FilterDispatcher类的init()方法处打下断点,可以明显看出从tomcat到struts2工作的整个流 程,大家都看看,把学到的跟小弟共享下.
2. 一个傻傻的问题,但是要真正把它弄清楚也不容易,Servlet,Filter,Intercept,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" class="attachmentAction"
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.打包下载性能真的很差,有没有更好的批量下载方法,请大家指出..谢谢
四.讨论struts2流程
1.我加载struts2的FilterDispatcher类的init()方法处打下断点,可以明显看出从tomcat到struts2工作的整个流 程,大家都看看,把学到的跟小弟共享下.
2. 一个傻傻的问题,但是要真正把它弄清楚也不容易,Servlet,Filter,Intercept,Struts2工作底层到底有何 联系..
相关推荐
在Struts2中,文件上传和下载是常见的功能需求,对于用户交互和数据交换至关重要。以下是对这些知识点的详细阐述: 1. **文件上传**: 在Struts2中,文件上传主要依赖于`Commons FileUpload`库,它是一个Apache提供...
总的来说,Struts2的文件上传和下载功能通过集成Apache Commons FileUpload库,大大简化了开发者的工作,提供了友好的API和配置选项,使得处理文件上传和下载变得轻而易举。开发者只需要关注业务逻辑,无需过多关注...
在Struts2中,文件上传和下载是常见的功能需求,特别是在处理用户交互和数据交换时。这篇博客文章提供的"struts2文件上传下载源代码"旨在帮助开发者理解和实现这些功能。 文件上传功能允许用户从他们的设备上传文件...
在Struts2中,文件上传和下载是常见的功能,对于用户交互和数据交换至关重要。以下是对这一主题的详细阐述。 1. **文件上传** - **MultipartResolver**: 在处理文件上传时,Struts2首先需要一个MultipartResolver...
这个“Struts2 文件上传和下载示例程序”是一个演示如何在Struts2中实现这两个功能的实例。 文件上传是Web应用程序中的一项重要功能,允许用户从本地计算机选择文件并将其传输到服务器。在Struts2中,这一过程涉及...
### Struts2文件上传与下载教程 #### 一、文件上传原理及实现 **1.1 基础概念** 文件上传是Web开发中的常见需求之一。在Struts2框架中,实现文件上传主要依赖于表单的`enctype`属性设置为`multipart/form-data`。...
Struts2是一个强大的Java web框架,它为开发者提供了丰富的功能,包括文件上传和下载。在Struts2中处理文件上传和下载是常见的需求,对于构建交互式的Web应用来说至关重要。以下将详细介绍Struts2中如何实现这两个...
在实际项目中,文件上传和下载功能是必不可少的,本实例将详细讲解如何在Struts2框架下实现单个文件及多个文件的上传与下载。 首先,我们需要在Struts2的配置文件(struts.xml)中添加相关的Action配置,以便处理文件...
Struts2是一个强大的MVC(模型-视图-控制器)框架,广泛应用于Java ...以上就是使用Struts2框架实现文件上传下载的基本步骤和关键知识点。在实际开发中,可以根据项目需求进行调整和优化,确保功能的稳定性和安全性。
Struts2 文件上传和下载是Web开发中常见的情景,主要涉及到HTML表单、HTTP请求、文件处理以及服务器端的响应。以下将详细介绍Struts2框架下如何进行文件的上传和下载。 首先,理解文件上传的基本原理至关重要。当在...
Struts2是一个流行的Java web框架,它为开发者提供了一种优雅的方式来构建动态、结构化的Web...希望本文能为你提供关于Struts2文件上传和下载的基本指导,进一步学习可参考给定的博文链接或其他相关文档和示例代码。
在Struts2中,文件上传和下载是常见的功能需求,主要用于处理用户在Web表单中提交的文件,如图片、文档等。下面将详细介绍Struts2中文件上传和下载的实现方法。 ### 1. 文件上传 #### 1.1 配置Struts2 首先,我们...
在这个特定的项目中,我们关注的是"struts2文件上传下载"的功能,这涉及到用户通过Web界面上传文件到服务器,以及从服务器下载文件到用户的设备。 文件上传是Web应用中的常见需求,例如用户可能需要提交图片、文档...
Struts2是一个强大的Java web框架,它为开发者提供了丰富的功能,包括处理用户表单提交、进行文件上传和下载。在Web应用中,文件上传和下载是常见的需求,例如用户上传头像、下载文档等。Struts2通过其Action类和...
例如,Struts 1和Struts 2在处理文件上传和下载的方式上有显著区别,Struts 2引入了更多面向Action的API和拦截器机制。 5. **源代码分析** 在提供的压缩包文件`upload`中,可能包含了Action类、Struts配置文件、...
这个压缩包包含了实现Struts2文件上传所需的全部jar包,这些库文件对于理解和实现文件上传功能至关重要。 首先,我们要了解Struts2文件上传的基本流程。当用户通过表单提交包含文件输入字段的请求时,Struts2框架会...
综上所述,Struts2文件上传下载和表单重复提交涉及多个技术点,包括Struts2的配置、文件操作、HTTP响应头设置、安全性和异常处理。理解并熟练掌握这些知识点,对于构建健壮的Web应用程序至关重要。