1.问题分析:
通过Datatables控件所展示的数据,如果在defaultContent一列中打算通过ajax请求完成基于Strut的文件下载,则由于Strut框架特性,常常出现文件被浏览器直接打开的问题。(即使在对应的Action进行了attachment配置也不可行)
2.问题解决原理:
Datatables的ajax请求仅用于发送到查找文件的Action(Ac1),并通过Ac1返回一个真正可以下载文件url,在success回调中调用windows.open方式打开,从而将异步请求改为了同步请求。其实现原理图如下所示:
3.代码说明
1.包含DataTable的Ajax请求Jsp页面
<%@ page language="java" import="java.util.*" pageEncoding="utf-8"%>
<%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>检定标准管理界面</title>
<link rel="stylesheet" type="text/css" href="css/jquery.dataTables.css">
<script type="text/javascript" charset="utf8"src="js/LibJS/jquery.min.js"></script>
<script type="text/javascript" charset="utf8"src="js/LibJS/jquery.dataTables.js"></script>
</head>
<body>
<!-- Html 标签用于静态创建table 元素 id为用于Js的初始化 -->
<table id="example" class="display" cellspacing="0" width="100%">
<thead>
<tr>
<th>标准设备类型</th>
<th>标准名称</th>
<th>标准适用设备</th>
<th>标准文件名</th>
<th>其他操作</th>
</tr>
</thead>
<tbody></tbody>
<!-- 必须包含 tbody节点-->
</table>
<!-- 利用Jquery 完成Datatable控件初始化与异步数据请求 -->
<script type="text/javascript">
$(document).ready(
function()
{
//表格初始化方法
var table=$("#example").DataTable(
{
//指定ajax相关参数,url即相应的action dataSrc为Json结果的keyname值
"ajax" : {
"url" : "StandardScan.action",//用于获取数据Action,
"dataSrc" : "ResultJson"
},
//将Json结果数据集的key绑定到对应列中,注意大小写对应
"columns" : [
{"data" : "devicetype"},
{"data" : "chineseName"},
{"data" : "applyDevice"},
{"data" : "fileName"},
//对于非数据的元素,可以通过指定html标签的形式完成
{"defaultContent" : "<a href=\"#\" id='export'>导出</a>"}]
});
//导出按钮事件监听
$("#example tbody").on( "click", "#export",
function ()
{
var data=table.row($(this).parents('tr')).data()//获取单击行的数据
var devicetype=data.devicetype;//获取关键值
var fileName=data.fileName;
//执行异步删除请求
$.ajax({
url:"StandardExport", //完成待下载文件查找的Action1
type: "post",
dataType : "text", //注意这里返回的text记录,即真正用于完成文件下载的url请求
data:
{
//传递文件查找关键字
"devicetype":devicetype,
"filename":fileName
},
success: function(result)
{
//通过返回的用url再次向服务器发送文件下载请求(确保该url,由http://开头,否则浏览器不能解析)
window.open(result.toString())
},
error: function(result)
{
alert("服务器异常,请检查服务器状态");
}
});
});
});
</script>
</body>
</html>
2.用于查找待下载文件并返回下载url的Ac_StandardExport
/**
* 用于标准文件导出的Action,实际此Action只用于合成文件下载的url,具体下载执行操作由Ac_Download执行
* @author cyoubo
*
*/
public class Ac_StandardExport extends ActionSupport implements Preparable
{
private static final long serialVersionUID = 107971309744984424L;
//查询服务
private IStandardQuery Query;
//文件关键字
private String devicetype;
private String fileName;
@Override
public void prepare() throws Exception
{
//获取项目Standard主文件夹
String standardfold=ServletActionContext.getServletContext().getRealPath("/")+"Standard/";
//构建查询服务
Query=new StandardQuery(standardfold);
}
@Override
public String execute() throws Exception
{
//准备返回文件下载的url字符串的输出流
HttpServletResponse response = ServletActionContext.getResponse();
response.setContentType("text/html;charset=UTF-8");
PrintWriter out = response.getWriter();
//判断当前标准文件是否存在
if(Query.findStandFile(DeviceType.createFromChinese(devicetype), fileName)!=null)
//生成下载用Url(combineStandardFullPath方法在本例中用于合成待下载文件在服务器中的路径)
out.print(Ac_Download.getActionUrl(combineStandardFullPath()));
else
out.print("Error");
return null;//由于返回的字符串,此处必须返回null以表示没有待跳转页面
}
/**
* @return 合成标准文件文件的完整路径
*/
private String combineStandardFullPath()
{
return "Standard/"+DeviceType.createFromChinese(devicetype).name().toUpperCase()+"/"+fileName+".xml";
}
//getter与setter 方法省略
}
3.用于基于Strut框架实现文件下载的Action_Dowload
/**
* 用于实现文件的专用Action,一般由getActionUrl(String fullPath)调用文件下载操作
* @author cyoubo
*/
public class Ac_Download extends ActionSupport
{
private static final long serialVersionUID = 3970709669947735057L;
/**
* 下载文件的文件名,建议Strut.xml的action配置文件中指定fileName=${fileName},以指定下载对话框的文件名
*/
private String fileName;
/**
* 现在文件在服务器中的全路径,
*/
private String fullPath;
/**
* @return 获取下载文件流对象
* @throws UnsupportedEncodingException
*/
public InputStream getDownloadFile() throws UnsupportedEncodingException
{
//从传递的文件全路径中获取文件名,用于在下载对话框中展示
String[] item=FileUtils.splitFullPath2(fullPath);
this.fileName=item[1]+"."+item[2];
//防止中文文件名为乱码的问题
this.fileName = new String(this.fileName.getBytes("GBK"),"ISO-8859-1");
return ServletActionContext.getServletContext().getResourceAsStream(fullPath) ;
}
//getter与setter 方法省略
/**
* 获取文件下载请求的url
* @param fullPath 从RootWeb目录开始(不包含RootWeb)的待下载文件全路径
* @return 可以开启文件下载Ac_Download action的url
*/
public static String getActionUrl(String fullPath)
{
return "http://localhost:8081/ExDevice2/FileDownloadAction.action?fullPath="+fullPath;
}
}
4.Strut.xml配置文件
<package name="StandardPackage" extends="struts-default">
<action name="StandardExport" class="com.dr.standard.action.Ac_StandardExport"></action>
<span style="white-space:pre"> </span><!-- 注意该Action没有结果JSP-->
</package>
<package name="OtherFunction" extends="struts-default">
<!-- 标准文件下载Action的配置-->
<action name="FileDownloadAction" class="com.dr.other.action.Ac_Download">
<result name="success" type="stream">
<param name="contentType">text/plain</param>
<param name="contentDisposition">attachment;fileName="${fileName}"</param>
<param name="inputName">downloadFile</param>
<param name="bufferSize">1024</param>
</result>
</action>
</package>