JavaScript导出Excel的三种实现方式及ActiveX内存释放详解

作者:木棉

时间:2026-10-28

浏览:93

本文详细讲解在IE环境下利用JavaScript将Web页面表格导出为Excel文档的三种具体实现方法。重点剖析了ActiveX对象在本地与远程文件操作中的权限凭证问题,并深入探讨了JavaScript中对象失效与内存释放的机制。通过调用CollectGarbage()或使用页面重载策略,有效解决了Excel进程残留及文件锁定的工程实际问题。

文章目录

JavaScript导出Excel的三种实现方式

本文介绍在Web开发中,使用JavaScript将页面数据导出为Excel文件的几种常见实现方案,供开发者参考。

具体代码实现如下:

<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8"/>
<title>WEB页面导出为EXCEL文档的方法</title>
</head>
<body>
<table id="tableExcel" width="100%" border="1" cellspacing="0" cellpadding="0">
<tr>
<td colspan="5" align="center">WEB页面导出为EXCEL文档的方法</td>
</tr>
<tr>
<td>列标题1</td>
<td>列标题2</td>
<td>列标题3</td>
<td>列标题4</td>
<td>列标题5</td>
</tr>
<tr>
<td>aaa</td>
<td>bbb</td>
<td>ccc</td>
<td>ddd</td>
<td>eee</td>
</tr>
<tr>
<td>AAA</td>
<td>BBB</td>
<td>CCC</td>
<td>DDD</td>
<td>EEE</td>
</tr>
<tr>
<td>FFF</td>
<td>GGG</td>
<td>HHH</td>
<td>III</td>
<td>JJJ</td>
</tr>
</table>
<input type="button" onclick="javascript:method1('tableExcel');" value="第一种方法导入到EXCEL">
<input type="button" onclick="javascript:method2('tableExcel');" value="第二种方法导入到EXCEL">
<input type="button" onclick="javascript:getXlsFromTbl('tableExcel',null);" value="第三种方法导入到EXCEL">
<SCRIPT LANGUAGE="javascript">
function method1(tableid) {//整个表格拷贝到EXCEL中
var curTbl = document.getElementByIdx_x_x(tableid);
var oXL = new ActiveXObject("Excel.Application");
//创建AX对象excel
var oWB = oXL.Workbooks.Add();
//获取workbook对象
var oSheet = oWB.ActiveSheet;
//激活当前sheet
var sel = document.body.createTextRange();
sel.moveToElementText(curTbl);
//把表格中的内容移到TextRange中
sel.select();
//全选TextRange中内容
sel.execCommand("Copy");
//复制TextRange中内容
oSheet.Paste();
//粘贴到活动的EXCEL中
oXL.Visible = true;
//设置excel可见属性
}
function method2(tableid) //读取表格中每个单元到EXCEL中
{
var curTbl = document.getElementByIdx_x_x(tableid);
var oXL = new ActiveXObject("Excel.Application");
//创建AX对象excel
var oWB = oXL.Workbooks.Add();
//获取workbook对象
var oSheet = oWB.ActiveSheet;
//激活当前sheet
var Lenr = curTbl.rows.length;
//取得表格行数
for (i = 0; i < Lenr; i++)
{
var Lenc = curTbl.rows(i).cells.length;
//取得每行的列数
for (j = 0; j < Lenc; j++)
{
oSheet.Cells(i + 1, j + 1).value = curTbl.rows(i).cells(j).innerText;
//赋值
}
}
oXL.Visible = true;
//设置excel可见属性
}
function getXlsFromTbl(inTblId, inWindow) {
try {
var allStr = "";
var curStr = "";
//alert("getXlsFromTbl");
if (inTblId != null && inTblId != "" && inTblId != "null") {
curStr = getTblData(inTblId, inWindow);
}
if (curStr != null) {
allStr += curStr;
}
else {
alert("你要导出的表不存在!");
return;
}
var fileName = getExcelFileName();
doFileExport(fileName, allStr);
}
catch(e) {
alert("导出发生异常:" + e.name + "->" + e.description + "!");
}
}
function getTblData(inTbl, inWindow) {
var rows = 0;
//alert("getTblData is " + inWindow);
var tblDocument = document;
if (!!inWindow && inWindow != "") {
if (!document.all(inWindow)) {
return null;
}
else {
tblDocument = eval_r(inWindow).document;
}
}
var curTbl = tbldocument.getElementByIdx_x_x(inTbl);
var outStr = "";
if (curTbl != null) {
for (var j = 0; j < curTbl.rows.length; j++) {
//alert("j is " + j);
for (var i = 0; i < curTbl.rows[j].cells.length; i++) {
//alert("i is " + i);
if (i == 0 && rows > 0) {
outStr += " ";
rows -= 1;
}
outStr += curTbl.rows[j].cells[i].innerText + " ";
if (curTbl.rows[j].cells[i].colSpan > 1) {
for (var k = 0; k < curTbl.rows[j].cells[i].colSpan - 1; k++) {
outStr += " ";
}
}
if (i == 0) {
if (rows == 0 && curTbl.rows[j].cells[i].rowSpan > 1) {
rows = curTbl.rows[j].cells[i].rowSpan - 1;
}
}
}
outStr += " ";
}
}
else {
outStr = null;
alert(inTbl + "不存在!");
}
return outStr;
}
function getExcelFileName() {
var d = new Date();
var curYear = d.getYear();
var curMonth = "" + (d.getMonth() + 1);
var curDate = "" + d.getDate();
var curHour = "" + d.getHours();
var curMinute = "" + d.getMinutes();
var curSecond = "" + d.getSeconds();
if (curMonth.length == 1) {
curMonth = "0" + curMonth;
}
if (curDate.length == 1) {
curDate = "0" + curDate;
}
if (curHour.length == 1) {
curHour = "0" + curHour;
}
if (curMinute.length == 1) {
curMinute = "0" + curMinute;
}
if (curSecond.length == 1) {
curSecond = "0" + curSecond;
}
var fileName = "leo_zhang" + "_" + curYear + curMonth + curDate + "_"
+ curHour + curMinute + curSecond + ".csv";
//alert(fileName);
return fileName;
}
function doFileExport(inName, inStr) {
var xlsWin = null;
if (!!document.all("glbHideFrm")) {
xlsWin = glbHideFrm;
}
else {
var width = 6;
var height = 4;
var openPara = "left=" + (window.screen.width / 2 - width / 2)
+ ",top=" + (window.screen.height / 2 - height / 2)
+ ",scrollbars=no,width=" + width + ",height=" + height;
xlsWin = window.open("", "_blank", openPara);
}
xlsWin.document.write(inStr);
xlsWin.document.close();
xlsWin.document.execCommand('Saveas', true, inName);
xlsWin.close();
}
</SCRIPT>
</body>
</html>

ActiveX对象内存释放与进程管理

接下来处理Excel进程关闭的相关问题。

具体代码实现如下:

// JavaScript中的析构问题(ActiveX Object示例)
//---------------------------------------------------------
<script>
var strSaveLocation = 'file:///E:/1.xls'
function createXLS() {
var excel = new ActiveXObject("Excel.Application");
var wk = excel.Workbooks.Add();
wk.SaveAs(strSaveLocation);
wk.Saved = true;
excel.Quit();
}
function writeXLS() {
 var excel = new ActiveXObject("Excel.Application");
 var wk = excel.Workbooks.Open(strSaveLocation);
 var sheet = wk.Worksheets(1);
 sheet.Cells(1, 1).Value = '测试字符串';
 wk.SaveAs(strSaveLocation);
 wk.Saved = true;
excel.Quit();
}
</script>
<body>
 <button onclick="createXLS()">创建</button>
 <button onclick="writeXLS()">重写</button>
</body>

在此示例中,若操作本地文件通常不会引发异常,仅可能产生少量内存垃圾。然而,当strSaveLocation指向远程URL时,本地会缓存文件存取权限凭证,且同一时间仅允许一个远程实例打开并存储该Excel文档。因此,反复点击“重写”按钮会导致异常。

请注意,这是SharePoint Services (SPS)环境中操作共享文件时的简化代码实例,属于工程实践中的真实问题,而非单纯的理论探讨。

解决该问题涉及两个关键点: ① 释放本地凭证 ② 释放ActiveX Object实例

首先从JavaScript中对象的“失效”机制入手。简而言之: ① 对象若处于其生存上下文之外,即视为失效。 ② 全局对象若无引用持有,即视为失效。

例如:

具体代码实现如下:

//---------------------------------------------------------
// JavaScript对象何时失效
//---------------------------------------------------------
function testObject() {
var _obj1 = new Object();
}
function testObject2() {
 var _obj2 = new Object();
 return _obj2;
}
// 示例1
testObject();
// 示例2
testObject2()
// 示例3
var obj3 = testObject2();
obj3 = null;
// 示例4
var obj4 = testObject2();
var arr = [obj4];
obj3 = null;
arr = [];

在上述四个示例中: - “示例1”在函数testObject()内部构造_obj1,函数退出时该对象离开上下文,因此失效; - “示例2”中,testObject2()构造_obj2并返回,使其拥有函数外的上下文,但因返回值未被变量持有,_obj2立即失效; - “示例3”中,_obj2被外部变量obj3持有,直到执行“obj3=null”后,因引用消失而失效。 - 同理,“示例4”中的_obj2在“arr=[]”执行后失效。

然而,对象“失效”并不等同于“释放”。JavaScript运行环境内部无法精确告知用户对象何时被释放,这取决于JavaScript的内存回收机制,与.NET的回收机制类似。

在前述Excel操作示例中,对象所有者即“EXCEL.EXE”进程仅在“ActiveX Object实例释放”后才会真正终止。文件锁及操作系统权限凭证与进程绑定,若对象仅“失效”而未“释放”,其他进程处理文件及权限凭证时将出现障碍。

有人将此归咎于JavaScript或COM机制的BUG,实则不然。这是操作系统、IE浏览器与JavaScript之间复杂交互的结果,并非独立问题。

Microsoft公开了应对策略:主动调用内存回收过程。

在微软的JScript中提供了CollectGarbage()过程(简称GC过程),用于清理当前IE环境中“失效的对象实例”,即调用对象的析构过程。

在前例中调用GC过程的代码如下:

具体代码实现如下:

//---------------------------------------------------------
// 处理ActiveX Object时,GC过程的标准调用方式
//---------------------------------------------------------
function writeXLS() {
//(略...)
excel.Quit();
 excel = null;
 setTimeout(CollectGarbage, 1);
}

第一行代码调用excel.Quit()以中止并退出Excel进程,但由于JavaScript环境仍持有excel对象实例,进程并未实际终止。

第二行代码将excel设为null,清除对象引用使其“失效”。但因对象仍在函数上下文内,直接调用GC过程仍无法清理该对象。

第三行代码使用setTimeout()调用CollectGarbage,时间间隔设为'1',确保GC过程在writeXLS()函数执行完毕后触发。此时excel对象满足“无引用”和“离开上下文”两个条件,可被GC清理。

在涉及ActiveX Object的JS环境中,使用GC过程非常有效。潜在的ActiveX Object包括XML、VML、OWC (Office Web Component)、Flash,甚至JS中的VBArray。

从这一角度看,采用XMLHTTP的Ajax架构需满足“不切换页面”特性,因此在适当时机主动调用GC过程,能获得更好的UI体验与效率。

事实上,即使使用GC过程,前述Excel问题仍无法完全解决,因为IE缓存了权限凭证。更新页面权限凭证的唯一方法是“切换到新页面”。因此,在提到的SPS项目中,采用的并非GC,而是以下代码:

具体代码实现如下:

//---------------------------------------------------------
// 处理ActiveX Object时采用的页面切换代码
//---------------------------------------------------------
function writeXLS() {
//(略...)
excel.Quit();
 excel = null;
 
 // 下面代码用于解决IE call Excel的一个BUG, MSDN中提供的方法:
 // setTimeout(CollectGarbage, 1);
 // 由于不能清除(或同步)网页的受信任状态, 所以将导致SaveAs()等方法在
 // 下次调用时无效.
 location.reload();
}

最后补充一点:当IE窗口被最小化时,IE会主动调用一次CollectGarbage()函数,这使得IE窗口在最小化后内存占用显著降低。

希望本文所述内容对大家基于JavaScript的Web程序设计有所帮助。

实用工具推荐

电子包浆

绿化、压缩、噪点和虚拟水印,自由调节做旧程度。

使用 ›
图片压缩

选择画质和输出格式,预览压缩前后的大小。

使用 ›
相关文章