首先说一下,ie浏览器有个ActiveXObject的属性,可以很方便的操作excel,网上也有很大教程,就不再赘述了,自行百度。
如果是非ie浏览器的,则需要用到html5的FileReader属性,以及网上的js-xlsx库。微软的xlsx格式本质上是一个zip。而FileReader可以将文件读取成二进制流,通过一定解码即可将zip的二进制流给解析出来,之后的处理sheet以及单元格内容就简单了。如果想要完全自己写,需要对xlsx的压缩编码机制有非常深入的了解。国内网上几乎搜不到半点资料,国外稍有一些,但也不简单,研究起来可能很费时间。国内网上几乎所有的关于用js读取excel的博客都是在用别人的库,而且基本上都是js-xlsx这个库,目前没有找到半篇是真的自己全部撸代码自行解决掉那个zip二进制流解析的部分的。
我向来不反对使用现成的轮子,但是js-xlsx库有点大,即使用js压缩处理也有400k,加上一个jszip大约100k,就需要500k了。这种体积还是比较大的。原本想自行实现解码,或者将js-xlsx中一些不需要的功能给剥离掉,但是时间精力有限,最后就算了,500k就500k吧,这已经算是小的了,如果用到更多的库或者不压缩那还不止。
另外,单页面应用是不推荐使用jquery的,无它,嫌大而已。而且大部分jquery的功能,都可以用zepto.js实现。
先说明一下,本文需用引入两个文件,分别是jszip.js还有xlsx.min.js,可以去github或者gitee上面找js-xlsx的库下载下来,其中的jszip.js还没有经过压缩,可以百度找个在线js代码压缩的网站把代码压缩一下。
接着是我的代码:
<!DOCTYPE html>
<html>
<head>
<title></title>
</head>
<body>
<input type="file" id="fileUpload" style="display:none;" onchange="uploadExcel()"/>
<input type="button" id="upload" value="upload" onclick="document.getElementById('fileUpload').click()">
</body>
<script type="text/javascript" src='jszip.min.js'></script>
<script type="text/javascript" src='xlsx.min.js'></script>
<script type="text/javascript">
var id = function(x){return document.getElementById(x);}
function uploadExcel(){
var fileUpload = id("fileUpload")
var nameWithPath = fileUpload.value.toLowerCase();
//用正则表达式校验是否是excel格式的文件
var regex = /^(.)+(\.xls|\.xlsx)$/;
if(regex.test(nameWithPath)){
if(typeof(FileReader) != "undefined"){
var reader = new FileReader();
// 非ie内核的浏览器
if(reader.readAsBinaryString){
reader.readAsBinaryString(fileUpload.files[0]);
reader.onload = function(e){
readExcel(e.target.result);
}
}
else{ //高版本的ie内核浏览器
reader.readAsArrayBuffer(fileUpload.files[0]);
reader.onload = function(e){
var data = "";
var bytes = new Unit8Array(e.target.result);
for(var i=0;i<bytes.byteLength;i++){
data += String.fromCharCode(bytes[i]);
}
readExcel(data);
}
}
}
else{
alert('您的浏览器不支持文件上传功能,建议使用谷歌浏览器');
}
}
else{
alert('您选择的文件不是excel文件,请重新选择');
}
}
var columns = [];
function transformLetters(l){
var ls = {'A':1,'B':2,'C':3,'D':4,'E':5,'F':6,'G':7,'H':8,'I':9,'J':10,'K':11,'L':12,'M':13,'N':14,'O':15,'P':16,'Q':17,'R':18,'S':19,'T':20,'U':21,'V':22,'W':23,'X':24,'Y':25,'Z':26}
var sum = 0
while(l.length){
sum += ls[l[0]]*Math.pow(26,l.length-1)
l = l.slice(1,l.length)
}
return sum
}
function readExcel(data){
var workbook = XLSX.read(data,{
type:'binary'
});
var sheet0 = workbook.Sheets[workbook.SheetNames[0]];
var arr = sheet0;
for(var i in arr){
if(i.match(/[A-Z]+[0-9]+/g)){
//column是列
var column = transformLetters(i.match(/[A-Z]+/g)[0].toUpperCase()) - 1;
if(columns[column]==undefined){
columns[column] = [];
}
var row = i.match(/[0-9]+/g)[0] - 1;
columns[column][row] = arr[i].w;
//t表示内容类型(其中的s表示string,n表示number,b表示boolean,d表示date
//v表示原始值
//f表示公式,如B2+C3
//h表示html内容
//w格式化后的内容
//r富文本内容rich text
//如果没有任何处理的话,w会把数字变成字符串
}
}
console.log(column);
}
</script>
</html>
有几个地方稍作解释,第一是上面的正则表达式,是验证是否为xlsx或xls文件,是再做进一步处理。
第二是这个XLSX库本身不能按列把数据输出来,所以需要进一步处理。先对sheet里面的所有内容用for in 打印出来,判断是否是 字母+数字 的属性名,形如 A1 B3这样的格式,
如果是,则对A1 B3这样的字母加数字拆分并且进行翻译,字母后面的数字就不用说了,就是单元格的行,这个好办,但是ABC这样的字母就要做处理了,如果是只有一个字母的,那么很简单,写一个js hash对象取对应的值就行了,但是我们知道当excel的列多于26的时候就会变成AA,多于702的时候就会变成AAA,以此类推。
本质上就是26进制翻译成10进制嘛,所以我就写了上面那个 transformLetters()的函数将26进制转换成10进制。
上面那个var arr= sheet0这一句其实是多余的,这么写只是自己看起来比较清晰而已,可以改掉。
读取后将值存到一个二维数组里面,就可以进行一系列的处理操作了。
我这里是根据我的业务口径需求按列存储数组,不是说一定要按列,可以自行改成按行处理。