最近做项目的时候自己再次用到了Ajax,可是这一次却偏偏出现了中文乱码,xmlHttp.responseText接受到的始终是 乱码,不管服务器端Header如何设置,始终是乱码,都烦扰了一个星期了。最后查看原来的Ajax代码,终于发现问题的所在。
错误的服务器端是如此设置:
PrintWriter out=response.getWriter();
/**从Ajax接受中文参数的时候设置的编码**/
request.setCharacterEncoding("gb2312");
/**从返回中文字符串的时候设置的编码**/
response.setCharacterEncoding("gb2312");
response.setContentType("text/html;");
response.setHeader("Content-Type","text/html;charset=gb2312");
/**设置缓存**/
response.setHeader("Cache-Control","no-cache");
response.setHeader("Pragma","no-cache");
resposne.setDateHeader("Expires","0");
通过和以前代码仔细比较,终于发现了很细小很容易被忽略的问题,就是代码的先后顺序。这里应该设置的是返回字符串的编码,所以,应该在输出对象之前设置编码。也就是把PrintWriter out=response.getWriter();放设置编码语句之后。
还有最近在项目中自己负责从各种各样的页面导出数据到Excel中。其中有table的行和列固定纯文本的导出;也有根据情况显示不同的行和列的table的情况的导出;更有把文字显示在文本框中的,同时文本框的颜色和背景显示一样的页面的Excel的导出。总结方法如下:
第一种
最快同时不耗内存的方法,就是直接把存放页面显示的数据对象【request.getAttribute("xx")】放在session中,当 点击按钮的时候,在服务器端按照网页显示的样式把数据存放到Excel中,然后直接输出出来。但是对于根据各种不同情况在页面显示不同样式的表格,就需要在服务器端也根据不同的情况把数据存入到Excel中以后再输出出来,这样就有点麻烦。
这里定义一个公用的方法用来获得写入Excel的三个对象
import java.io.*;
import java.util.*;
import jxl.write.WritableWorkbook;
import jxl.*;
import jxl.write.*;
import jxl.format.*;
import jxl.format.Alignment;
import jxl.format.Border;
import jxl.format.BorderLineStyle;
import jxl.format.VerticalAlignment;
/**
* 在此方法中把一些Excel布局的设置设置成功了以后
* 把设置后的对象返回,这样做带了代码的最小行数
* @param os
* @return
* @throws Exception
*/
@SuppressWarnings("unchecked")
public static List<Object> getWorkBook(FileOutputStream os)
throws Exception {
List<Object> list = new ArrayList<Object>();
WorkbookSettings settings = new WorkbookSettings();
settings.setEncoding("ISO-8859-1");// 设置编码
// 创建一个Excel
WritableWorkbook book = Workbook.createWorkbook(os, settings);
// 创建一个名字为TestSheet并且索引位置为0的工作薄
WritableSheet ws = book.createSheet("TestSheet", 0);
// 这是工作薄的样式:字体格式,字体大小,是否加粗,是否有下划线,字体颜色
WritableFont wfc = new WritableFont(WritableFont.ARIAL, 12,
WritableFont.NO_BOLD, false, UnderlineStyle.NO_UNDERLINE,
jxl.format.Colour.BLACK);
// 格式化内容
WritableCellFormat wcfFC = new WritableCellFormat(wfc);
// 设置工作薄的背景颜色
//wcfFC.setBackground(jxl.format.Colour.WHITE);
// 设置字体的垂直位置
wcfFC.setVerticalAlignment(VerticalAlignment.CENTRE);
// 设置字体的水平位置
wcfFC.setAlignment(Alignment.CENTRE);
// 把当前的WorkBook返回
list.add(book);
// 把当前的工作薄返回
list.add(ws);
// 把格式返回
list.add(wcfFC);
return list;
}
获得这三个对象以后,就直接可以把数据写入Excel中
String url = "d:/test.xls";
List<Object> list = (List<Object>) getWorkBook(new FileOutputStream(
new File(url)));
WritableWorkbook workbook = null;
WritableSheet sheet = null;
WritableCellFormat format = null;
// 得到设置后的Excel写入对象
if (list.size() != 0 && list.size() == 3) {
workbook = (WritableWorkbook) list.get(0);
sheet = (WritableSheet) list.get(1);
format = (WritableCellFormat) list.get(2);
}
List<QueryResultRow> queryResult = (List<QueryResultRow>) request
.getSession(true).getAttribute("queryResult");
if (queryResult != null) {
for (int i = 0; i < queryResult.size(); i++) {
List<String> rowDataList = (List<String>) queryResult
.get(i).getRowData();
for (int j = 0; j < rowDataList.size(); j++) {
String str = rowDataList.get(j);
if (str.equals("") || str == null) {
str = " ";
}
/**写入Excel**/
Label label = new Label(j, i, str, format);
sheet.setColumnView(j, 30);
sheet.addCell(label);
}
}
workbook.write();
workbook.close();
}
然后,把刚创建的Excel输出:
/**
* 读取Excel,并打开指定位置Excel文件
*/
// 设置文件类型
response.setContentType("application/vnd.ms-excel");
// 设置文件的打开方式
response.setHeader("Content-disposition", "attachment;filename=/""
+ "test.xls" + "/";");
/**
* 下面为读取Excel的内容
*/
try {
ServletOutputStream sos = response.getOutputStream();
FileInputStream fis = new FileInputStream(url);
BufferedOutputStream bos = new BufferedOutputStream(sos);
byte[] bytes = new byte[2000000];
bos.write(bytes, 0, fis.read(bytes));
fis.close();
sos.close();
bos.close();
}
catch (Exception ex) {
ex.printStackTrace();
}
如果页面中table的显示行和列是不确定的或者根据各种不同条件进行不同行数和列数显示的,可以选择遍历table的每一行的每一列的innerText,
然后通过Ajax把每一行的每一列的数据传递到Controller(Struts为Action)中,然后把每一行的内容存放到List集合对象中,如果遍历table结束
再把List的每一个元素取出来,最后通过前面定义的公用方法(getWorkBook)获得操作Excel的三大对象把每一行的数据写入到Excel中并显示出来
var xmlHttp;
function createXmlHttp()
{
if(window.ActiveXObject)
{
xmlHttp=new ActiveXObject("Microsoft.XMLHTTP");
}
else if(window.XMLHttpRequest)
{
xmlHttp=new XMLHttpRequest();
}
}
function startRequest(str)
{
var url="/sxdd/water/daily/daily.do?method=impData&str="+str;
createXmlHttp();
xmlHttp.open("GET",url,true);
xmlHttp.onreadystatechange=handleState;
xmlHttp.send(null);
}
function handleState()
{
if(xmlHttp.readyState==4)
{
if(xmlHttp.status==200)
{
if(xmlHttp.responseText=="success")
{
location.href="/sxdd/water/daily/daily.do?method=showExcel";
}
}
}
}
/**第一中遍历表格的方法**/
function fetchCellData()
{
//得到表格体
var dailyBody=document.frames('dayData').document.getElementById("dailyBody");
var str="";
if(dailyBody.hasChildNodes())
{
//获得其中的行数
var trLength=dailyBody.childNodes.length;
for(var i=0;i<trLength;i++)
{
//行
var dailyTr=dailyBody.childNodes[i];
if(dailyTr.hasChildNodes())
{
var tdLength=dailyTr.childNodes.length;
for(var j=0;j<tdLength;j++)
{
//列
var dailyTd=dailyTr.childNodes[j];
if(dailyTd.hasChildNodes())
{
if(dailyTd.firstChild!=null)
{
var data=dailyTd.firstChild.data;
str+=data+"~";
}
if(dailyTd.firstChild==null)
{
str+=""+"~";
}
}
else
{
str+=""+"~";
}
}
//去掉最后一行的最后一列的一个字符"~"
str=str.substr(0,str.length-1);
//alert(str);
startRequest(str);
//重新初始化str,防止影响后面的代码
str="";
}
}
startRequest("over");
}
}
/**第二中遍历表格的方法**/
function kk()
{
var str="";
var tab=document.frames["dayData"].document.getElementById('excelTable');
for(var i=0;i<tab.rows.length;i++)
{
for(var j=0;j<tab.rows[i].cells.length;j++)
{
//alert('td为'+tab.rows[i].cells[j].innerHTML);
var text=tab.rows[i].cells[j].innerText;
if(text==null||text.length==0)
{
str+=""+"~";
}
else{
str+=text+"~";
}
}
//去掉最后一行的最后一列的一个字符"~"
str=str.substr(0,str.length-1);
//alert(str);
startRequest(str);
//重新初始化str,防止影响后面的代码
str="";
}
startRequest("over");
}
遍历table表格这两种方法,有时候会出现问题,可能是第二行的数据比第一行先读取,不晓得为什么?难道是IE的问题???
服务器端:
// 导出数据到Excel中
public ModelAndView impData(HttpServletRequest request,
HttpServletResponse response) throws Exception {
boolean flag=false;
response.setCharacterEncoding("utf-8");
response.setHeader("Cache-Control", "no-cache");
response.setHeader("Pragma", "no-cache");
response.setDateHeader("Expires", 0);
response.setContentType("text/html;charset=utf-8");
PrintWriter out=response.getWriter();
// 存储每一行的数据
List<String> childList = new ArrayList<String>();
if (!request.getParameter("str").equals("")
&& request.getParameter("str") != null) {
// 判断是否是最后一行数据
if (!request.getParameter("str").equals("over")) {
String str = request.getParameter("str");
// 转换中文
str = new String(str.getBytes("ISO-8859-1"), "gb2312");
// 通过一个特殊的字符来判断每一列
String strs[] = str.split("~");
if (strs.length != 0) {
for (String s : strs) {
if(StringUtils.contains(s,"?"))
{
childList.add("");
}
else {
childList.add(s);
}
}
list.add(childList);
}
} else {
String url = "d:/test.xls";
List<Object> l = (List<Object>) getWorkBook(new FileOutputStream(new File(url)));
WritableWorkbook workbook = null;
WritableSheet sheet = null;
WritableCellFormat format = null;
// 得到设置后的Excel写入对象
if (l.size() != 0 && l.size() == 3) {
workbook = (WritableWorkbook) l.get(0);
sheet = (WritableSheet) l.get(1);
format = (WritableCellFormat) l.get(2);
}
if (list.size() != 0) {
for (int i = 0; i < list.size(); i++) {
List<String> li = list.get(i);
for (int j = 0; j < li.size(); j++) {
String s = li.get(j);
Label label = new Label(j, i, s, format);
sheet.setColumnView(j, 30);
sheet.addCell(label);
}
}
workbook.write();
workbook.close();
//写入到Excel以后,把list中的元素清除了。省的影响以后的数据
list.clear();
//写入成功了才能返回成功
flag=true;
}
}
if(flag)
{
out.print("success");
}
else
{
out.print("continue");
}
}
return null;
}
/**如果写入成功,把Excel显示出来**/
public ModelAndView showExcel(HttpServletRequest request,HttpServletResponse response) throws Exception
{
String url = "d:/test.xls";
/**
* 读取Excel,并打开指定位置Excel文件
*/
// 设置文件类型
response.setContentType("application/vnd.ms-excel");
// 设置文件的打开方式
response.setHeader("Content-disposition",
"attachment;filename=/"" + "test.xls" + "/";");
/**
* 下面为读取Excel的内容
*/
try {
ServletOutputStream sos = response.getOutputStream();
FileInputStream fis = new FileInputStream(url);
BufferedOutputStream bos = new BufferedOutputStream(sos);
byte[] bytes = new byte[10000000];
bos.write(bytes, 0, fis.read(bytes));
bos.close();
fis.close();
sos.close();
} catch (Exception ex) {
ex.printStackTrace();
System.out.println("输出Excel有误");
}
return null;
}
第三种方法:
如果table的td中含有其他的控件(text,hidden),页面显示的值是在控件中,并且控件的背景颜色和table的背景颜色一致,
这就要在导出之前把td中的控件中的值取出来,同时把控件remove掉,最后可以选择
①按照前面的第二种方法遍历处理后table
②如果要导出的数据不是很大,可以选择把处理后的table放到页面某一个隐藏控件中,当点击导出的时候把此隐藏控件的内容提交到服务器端,
然后把这些内容放到request的属性中,在另一个jsp页面设置表头直接以Excel的形式把数据显示在Excel中。
function impExcel()
{
var temp;
var table=document.getElementById("table");
var tbody=table.childNodes[0];
/**删除最后一个对象**/
tbody.removeChild(tbody.lastChild);
var trs=tbody.childNodes;
for(var i=0;i<trs.length;i++)
{
/**获得每一行,每一行中有多列**/
var tds=trs[i].childNodes;
for(var j=0;j<tds.length;j++)
{
/**获得每一列**/
var td=tds[j];
//alert(td.childNodes.length);
/**每一列中可能有多个元素**/
for(var k=0;k<td.childNodes.length;k++)
{
if(td.childNodes[k].tagName=="INPUT")
{
//alert(td.childNodes[k].type+"----"+td.childNodes[k].name);
if(td.childNodes[k].type=="hidden")
{
td.removeChild(td.childNodes[k]);
}
if(td.childNodes[k].type=="text")
{
var text=td.innerText;
var value=td.childNodes[k].value;
temp=value+text;
td.removeChild(td.childNodes[k]);
td.innerText=temp;
}
}
}
}
}
document.getElementById("content").value=table.innerHTML;
document.myform.submit();
}
public ActionForward impExcel(ActionMapping mapping, ActionForm form,
HttpServletRequest request, HttpServletResponse response)
throws Exception {
request.setCharacterEncoding("gb2312");
String content = request.getParameter("content");
request.setAttribute("content", content);
return mapping.findForward("impExcel");
}
<%@page contentType="text/html; charset=gb2312"%>
<%
response.setHeader("Cache-Control", "no-cache");
response.setHeader("Pragma", "no-cache");
response.setDateHeader("Expires", 0);
response.setContentType("application/vnd.ms-excel");
response.setHeader("Content-disposition",
"attachment;filename=expense.xls");
%>
<html>
<head>
<script type="text/javascript">
</script>
</head>
<meta http-equiv='Content-Type' content='tex/html;charset=gb2312'/>
<body>
<table id="table">
<%=request.getAttribute("content") %>
</table>
</body>
</html>
当然,如果点击在一个页面中点击导出,查询的数据直接以Excel的形式显示很方便,只需要像上面一样设置表头就可以了:
<%
response.setHeader("Cache-Control", "no-cache");
response.setHeader("Pragma", "no-cache");
response.setDateHeader("Expires", 0);
response.setContentType("application/vnd.ms-excel");
response.setHeader("Content-disposition",
"attachment;filename=expense.xls");
%>
当jsp页面包含此设置的时候,此jsp页面就会以Excel的形式打开。
但是,最近项目的导出Excel遇到了一个新的问题:导出的数据列要进行累计求和。郁闷,这如何解决???
毕竟有些td的innerText是文本,有的td的inner是数字,这如何进行判断???