解决 tableExport 导出 Excel 中文乱码及无响应问题
在开发数据展示类 Web 应用时,表格导出功能几乎是标配。然而,当使用 tableExport 这个流行 jQuery 插件将 HTML 表格导出为 Excel 文件时,不少开发者都遇到过两个令人头疼的问题:点击“导出”按钮后毫无反应,或者导出的文件中中文变成了一串类似“æŽå°é¾”的乱码字符。
这些问题在国内项目中尤为突出——毕竟谁不想让“张三”、“李小龙”这些名字清清楚楚地出现在报表里呢?更糟的是,某些用户反馈说点了好几次都没反应,还以为系统坏了。其实问题不在他们,而在于插件默认行为与现代浏览器安全策略之间的冲突。
要真正解决这两个问题,不能只改配置,还得深入代码层面理解其运作机制,并做出针对性优化。
我们先来看“无响应”的根源。早期的 tableExport 实现依赖 window.open('data:application/vnd.ms-excel;base64,...') 来触发下载。这在技术上是可行的,但现代浏览器普遍会对非用户直接交互产生的弹窗进行拦截。即使你确实在点击按钮后调用了这个方法,只要不是立即执行(比如经过一层回调),就很可能被判定为“不安全操作”而静默阻止。
此外,IE 以外的主流浏览器虽然支持 Data URL,但在处理大体积 Base64 字符串时也可能出现性能卡顿甚至崩溃。因此,单纯拼接字符串再通过 window.open 打开的方式已经过时。
另一个问题是中文乱码。很多人第一反应是“是不是没设 UTF-8?”确实如此,但真相更复杂一些。HTML 表格本身可能是 UTF-8 编码的页面,但当你把内容提取出来生成一个内嵌在 Data URI 中的 HTML 片段时,如果没有显式声明 <meta charset="UTF-8">,Office 软件(尤其是老版本 Excel)会按照系统默认编码(如 Windows-1252 或 GBK)去解析,结果自然就是乱码。
更深层的原因是 JavaScript 原生的 btoa() 函数只能处理单字节字符,遇到 Unicode 字符(包括所有中文)就会抛错或输出错误字节流。如果不先用 encodeURIComponent 转义成百分号编码格式,再用 unescape 拆解为原始字节序列,就无法正确编码多字节字符。
所以,真正的解决方案必须从两方面入手:一是改写导出逻辑,采用更现代、更可靠的文件生成方式;二是确保整个流程中对中文字符的编码转换无遗漏。
使用 Blob 和 a 标签替代 window.open
现代浏览器提供了更好的选择:Blob 对象和 URL.createObjectURL() 方法。我们可以将构造好的 HTML 内容封装成一个具有正确 MIME 类型的二进制对象,然后创建一个隐藏的 <a> 元素,模拟点击事件来触发下载。这种方式不会触发弹窗拦截,且能处理更大的数据量。
同时,在生成的 HTML 文档头部加入 <meta charset="UTF-8">,明确告诉 Excel 使用 UTF-8 解码,避免乱码。
下面是经过实战验证的 tableExport.js 改进版本:
(function ($) {
$.fn.extend({
tableExport: function (options) {
var defaults = {
separator: ',',
ignoreColumn: [],
tableName: 'myTable',
type: 'csv',
pdfFontSize: 14,
pdfLeftMargin: 20,
escape: 'true',
htmlContent: 'false',
consoleLog: 'false'
};
var options = $.extend(defaults, options);
var el = this;
// 统一编码函数:确保 UTF-8 支持
function encodeUtf8(s) {
return unescape(encodeURIComponent(s));
}
if (options.type === 'excel' || options.type === 'doc') {
var excel = '<table>';
// 处理表头
$(el).find('thead tr').each(function () {
excel += '<tr>';
$(this).find('th').each(function (index) {
if ($(this).css('display') !== 'none' && options.ignoreColumn.indexOf(index) === -1) {
var colspan = $(this).attr('colspan') || 1;
excel += '<td style="text-align:center; font-weight:bold;" colspan="' + colspan + '">' +
encodeUtf8($(this).text().trim()) + '</td>';
}
});
$(this).find('td').each(function (index) {
if ($(this).css('display') !== 'none' && options.ignoreColumn.indexOf(index) === -1) {
var rowspan = $(this).attr('rowspan') || 1;
var colspan = $(this).attr('colspan') || 1;
excel += '<td rowspan="' + rowspan + '" colspan="' + colspan + '">' +
encodeUtf8($(this).text().trim()) + '</td>';
}
});
excel += '</tr>';
});
// 处理表体
$(el).find('tbody tr').each(function () {
excel += '<tr>';
$(this).find('td').each(function (index) {
if ($(this).css('display') !== 'none' && options.ignoreColumn.indexOf(index) === -1) {
var rowspan = $(this).attr('rowspan') || 1;
excel += '<td rowspan="' + rowspan + '">' + encodeUtf8($(this).text().trim()) + '</td>';
}
});
excel += '</tr>';
});
excel += '</table>';
// 构造完整的 HTML 文档结构,显式声明 UTF-8
var excelFile =
'<html xmlns:o="urn:schemas-microsoft-com:office:office" ' +
'xmlns:x="urn:schemas-microsoft-com:office:' + options.type + '" ' +
'xmlns="http://www.w3.org/TR/REC-html40">' +
'<head><meta charset="UTF-8"><title>Export Table</title></head>' +
'<body>' + excel + '</body></html>';
// 创建 Blob 并生成可下载链接
var blob = new Blob([excelFile], { type: 'application/vnd.ms-' + options.type });
var link = document.createElement('a');
link.href = URL.createObjectURL(blob);
link.download = options.tableName + '.' + (options.type === 'excel' ? 'xls' : options.type);
link.style.display = 'none';
document.body.appendChild(link);
link.click();
document.body.removeChild(link);
// 清理内存引用
setTimeout(function () {
URL.revokeObjectURL(link.href);
}, 100);
} else {
console.warn('其他格式暂未展开,请根据需求扩展');
}
}
});
})(jQuery);
可以看到,关键改动集中在:
- 所有文本内容在插入前都经过 encodeUtf8() 处理;
- 输出的 HTML 包含 <meta charset="UTF-8">;
- 使用 Blob + URL.createObjectURL 替代 window.open(base64);
- 利用 <a download> 属性控制文件名。
这套组合拳下来,无论是 Chrome、Firefox 还是 Edge,都能顺利导出带中文的 Excel 文件,且不会被拦截。
至于 jquery.base64.js,虽然新版 tableExport 已不再强制依赖它,但如果项目中有其他模块仍在使用该工具库,建议也同步升级以支持 UTF-8。以下是推荐的增强版实现:
(function ($) {
$.base64 = function (options) {
var defaults = {
data: '',
type: 0, // 0: encode, 1: decode
unicode: true
};
var opts = $.extend(defaults, options);
if (!opts.data) return '';
if (opts.unicode) {
if (opts.type === 0) {
// 编码:支持中文 UTF-8
return btoa(unescape(encodeURIComponent(opts.data)));
} else {
// 解码:还原中文
return decodeURIComponent(escape(atob(opts.data)));
}
} else {
return opts.type === 0 ? btoa(opts.data) : atob(opts.data);
}
};
})(jQuery);
这里的关键点是:永远不要直接对包含中文的字符串调用 btoa()。必须先通过 encodeURIComponent 转为百分号编码,再用 unescape 将其转为可以被 btoa 处理的字节流。
结合上述修改,最终的使用方式非常简洁:
<script src="https://code.jquery.com/jquery-3.6.0.min.js"></script>
<script src="./tableExport.js"></script>
<script src="./jquery.base64.js"></script>
<table id="myTable">
<thead>
<tr>
<th>姓名</th>
<th>城市</th>
<th>备注</th>
</tr>
</thead>
<tbody>
<tr>
<td>张三</td>
<td>北京</td>
<td>优秀员工</td>
</tr>
<tr>
<td>李小龙</td>
<td>上海</td>
<td>技术骨干</td>
</tr>
</tbody>
</table>
<button onclick="$('#myTable').tableExport({
type: 'excel',
tableName: '员工信息表'
})">导出 Excel</button>
只需一次调用,就能得到一份格式完整、中文清晰的 .xls 文件。
当然,也有几点需要注意:
- 不支持 IE 浏览器:因为 IE 不完全支持
Blob和URL.createObjectURL。如果你仍需兼容 IE,建议引入 FileSaver.js 作为 Polyfill。 - 推荐在 HTTPS 环境下测试,某些浏览器对 HTTP 页面中的 Blob URL 有限制。
- 若表格含有图片或其他复杂元素,可能需要额外处理资源嵌入逻辑。
目前 GitHub 上已有社区维护的改进版插件 hhurz/tableExport.jquery.plugin,集成了类似优化。但对于定制化要求较高的项目,掌握底层原理并自行封装仍是更稳妥的选择。
这种基于 Blob 和显式编码声明的导出模式,其实不仅适用于表格,也可以迁移到日志导出、配置备份、报表生成等场景。前端不再只是“显示数据”,而是有能力“带走数据”。
当你下次看到用户顺畅地点击“导出”并立刻收到一份干净整齐的 Excel 文件时,那种满足感,大概就像看到自己写的代码跑通了第一个自动化测试一样纯粹。
更新时间:2025年4月
作者:AI 技术文档工程师
环境测试:Chrome v120+, jQuery 3.x, Windows 10 / macOS Sonoma

2万+


被折叠的 条评论
为什么被折叠?



