关于js-xlsx的使用

写在前头,本人是名Java开发人员,偶尔在前端打打酱油,写出的代码或许存在问题,请路过的大神一一指正,不吝感激。

   最近公司准备做一些关于Excel 数据导入和导出相关需求,之前有在开源社区看到说比起纯后端解析,前端更有优势,一来是现在的个人电脑的性能已经有了长足的进步,而来,服务端的资源本就金贵,后端服务的瓶颈就是业务系统平台的瓶颈,对于服务端的优化,本就是一个永久的话题,说到这里,基本上也就该说今天的主角了,js-xlsx。

  要说js-xlsx,就不得不说xlsx,为什么呢,我也是在刚接触是一头雾水,因为在npm上搜索xlsx,第一条就是xlsx,但是进去之后就蒙了,怎么是js-xlsx呢?不信你看看。那在npm上搜索js-xlsx呢,进去之后却看到,xlsx-style,???如果你足够细心的话可能还会看到一个包,那就是xlsx-style,看到这里你估计就该问了,xlsx,js-xlsx,xlsx-style这些都是什么鬼,我说下我的理解吧,这些都是xlsx的分支,只不过由于xlsx的部分功能问题,其他人在xlsx的基础上衍生出了很多版本,比如还未提到的node-xlsx,以及鄙人的sognyz-xlsx,这些都是或多或少的引用了官方代码,在此基础上,进行了扩展开发,至于该怎么用,我说下我的看法,xlsx应该是bug最少也最稳定的,至少人家是鼻祖,关于xlsx-style和js-xlsx,它们在原有的功能基础上,添加了对导出样式的控制,让导出的Excel更加满足业务需要,比如说一些常见的设置字体样式,大小,颜色等待,但是我使用cdn方式引入xlsx-style时没有问题,但是使用ES6 import 语法是出现小问题(网上查询页面解决),在这个过程中,遇见了node-xlsx和js-xlsx,简单使用之后,发现js-xlsx是我要找的,node-xlsx是在js-xlsx的基础之上进行的一层薄薄的封装,不过这层封装也大大降低了js-xlsx的上手难度(值得自己学习),啰里啰嗦低讲到这里你估计又想说了,那就使用被,还费什么话,额~~~废话少说,捡重点的~~~

  先说关于导入日期处理这块,导入的文件中包含三种日期格式,截图如下,关于代码信息,在文章末尾处

excel截图

当我看到数据时,我得内心是慌乱的一逼,截图说明下

js-xlsx将数据直接解析成了个性化数据,浏览过源码就会发现,它是根据excel中的格式进行的格式化,虽然未必能转换成跟office中一模一样,但是确实实现了一大部分,但是这种数据丢给我的程序,我岂不是要凉凉,我们当然是希望他们给我们一种统一的格式(yyyy-MM-dd hh:mm或者时间戳格式),这样才方便自己程序处理,这个问题先记下,

 关于数字的问题,Excel截图如下,我的文件

 

解析,看数据,截图说明

哪里有两个问题,价格的值,莫名的多了个空格,而且还是字符串格式,身份证号的值,竟然使用了科学计数法,这,这,好牛B的程序,然而呢。。。我想静静......

js-xlsx虽然很强大哦,但是他并没有暴露出来一些关于处理数据的入口,哎,思来想去,要不自己改改?

然后就有了,

npm上的 songyz-xlsx

github上的 songyz-xlsx

 

另外,相关代码

 1 //表头单元格样式
 2 export const titleStyle = {
 3     font: {
 4         bold: true,
 5     },
 6     alignment: {
 7         horizontal: "center",
 8         vertical: "center",
 9     },
10     border: {
11         top: {
12             style: "thin",
13         },
14         bottom: {
15             style: "thin",
16         },
17         left: {
18             style: "thin",
19         },
20         right: {
21             style: "thin",
22         },
23     }
24 };
25 //内容单元格样式
26 export const bodyStyle = {
27     alignment: {
28         vertical: "center",
29     },
30     border: {
31         top: {
32             style: "thin",
33         },
34         bottom: {
35             style: "thin",
36         },
37         left: {
38             style: "thin",
39         },
40         right: {
41             style: "thin",
42         },
43     }
44 };
xlsx-support/common.js
 1 // 将指定的自然数转换为26进制表示。映射关系:[0-25] -> [A-Z]。
 2 export const getCharCol = (n) => {
 3     let s = '',
 4         m = 0
 5     while (n > 0) {
 6         m = n % 26 + 1
 7         s = String.fromCharCode(m + 64) + s
 8         n = (n - m) / 26
 9     }
10     return s
11 }
12 
13 //将数据写到文件中
14 export const writeFile = (fname, data, enc) => {
15     /*global IE_SaveFile, Blob, navigator, saveAs, URL, document, File, chrome */
16     if (typeof IE_SaveFile !== 'undefined') return IE_SaveFile(data, fname);
17     if (typeof Blob !== 'undefined') {
18         var blob = new Blob([blobify(data)], { type: "application/octet-stream" });
19         if (typeof navigator !== 'undefined' && navigator.msSaveBlob) return navigator.msSaveBlob(blob, fname);
20         if (typeof saveAs !== 'undefined') return saveAs(blob, fname);
21         if (typeof URL !== 'undefined' && typeof document !== 'undefined' && document.createElement && URL.createObjectURL) {
22             var url = URL.createObjectURL(blob);
23             if (typeof chrome === 'object' && typeof(chrome.downloads || {}).download == "function") {
24                 if (URL.revokeObjectURL && typeof setTimeout !== 'undefined') setTimeout(function() { URL.revokeObjectURL(url); }, 60000);
25                 return chrome.downloads.download({ url: url, filename: fname, saveAs: true });
26             }
27             var a = document.createElement("a");
28             if (a.download != null) {
29                 a.download = fname;
30                 a.href = url;
31                 document.body.appendChild(a);
32                 a.click();
33                 document.body.removeChild(a);
34                 if (URL.revokeObjectURL && typeof setTimeout !== 'undefined') setTimeout(function() { URL.revokeObjectURL(url); }, 60000);
35                 return url;
36             }
37         }
38     }
39     // $FlowIgnore
40     if (typeof $ !== 'undefined' && typeof File !== 'undefined' && typeof Folder !== 'undefined') try { // extendscript
41         // $FlowIgnore
42         var out = File(fname);
43         out.open("w");
44         out.encoding = "binary";
45         if (Array.isArray(payload)) payload = a2s(payload);
46         out.write(payload);
47         out.close();
48         return payload;
49     } catch (e) { if (!e.message || !e.message.match(/onstruct/)) throw e; }
50     throw new Error("cannot save file " + fname);
51 }
52 
53 /* normalize data for blob ctor */
54 function blobify(data) {
55     if (typeof data === "string") return s2ab(data);
56     if (Array.isArray(data)) return a2u(data);
57     return data;
58 }
59 
60 function s2ab(s) {
61     if (typeof ArrayBuffer === 'undefined') return s2a(s);
62     var buf = new ArrayBuffer(s.length),
63         view = new Uint8Array(buf);
64     for (var i = 0; i != s.length; ++i) view[i] = s.charCodeAt(i) & 0xFF;
65     return buf;
66 }
67 
68 function a2u(data) {
69     if (typeof Uint8Array === 'undefined') throw new Error("Unsupported");
70     return new Uint8Array(data);
71 }
xlsx-support/util.js
 1 import XLSX from 'songyz-xlsx'
 2 
 3 import { titleStyle, bodyStyle } from './xlsx-support/common'
 4 import { getCharCol, writeFile } from './xlsx-support/util'
 5 
 6 //导入文件的类型
 7 export const xlsxTypes = ["xlsx", "xlc", "xlm", "xls", "xlt", "xlw", "csv"];
 8 
 9 //导入文件
10 export const importSlsx = (file, opts) => {
11     return new Promise(function (resolve, reject) {
12         const reader = new FileReader()
13         reader.onload = function (e) {
14             opts = opts || {};
15 
16             opts.type = 'binary';
17             opts._dateType = opts._dateType || 1; //1,"yyyy-MM-dd hh:mm",2,时间戳
18             opts._numberType = opts._numberType || 1; //1,不适用科学计数法,2,使用科学计数法
19 
20             const wb = XLSX.read(e.target.result, opts);
21             resolve(Object.keys(wb.Sheets).map(key => XLSX.utils.sheet_to_json(wb.Sheets[key])).reduce((prev, next) => prev.concat(next)))
22         }
23         reader.readAsBinaryString(file.raw)
24     })
25 }
26 
27 //导出数据
28 export const exportXlsx = (dataArray, fileName) => {
29     let type = 'xlsx';
30     dataArray = dataArray || [{}];
31     fileName = fileName || 'file';
32 
33     var keyMap = Object.keys(dataArray[0]);
34     var title = {};
35     keyMap.forEach(key => title[key] = key);
36     dataArray.unshift(title);
37 
38     //用来保存转换好的json 
39     var sheetData = [];
40 
41     dataArray.map((row, i) => {
42         let style = i == 0 ? titleStyle : bodyStyle;
43         return keyMap.map((key, j) => {
44             return {
45                 style: style,
46                 value: row[key],
47                 position: (j > 25 ? getCharCol(j) : String.fromCharCode(65 + j)) + (i + 1)
48             };
49         })
50     }).reduce((prev, next) => prev.concat(next)).forEach((cell, i) =>
51         sheetData[cell.position] = {
52             v: cell.value,
53             s: cell.style
54         }
55     );
56     var outputPos = Object.keys(sheetData); //设置区域,比如表格从A1到D10
57 
58     var wb = {
59         SheetNames: ['mySheet'], //保存的表标题
60         Sheets: {
61             'mySheet': Object.assign({},
62                 sheetData, //内容
63                 {
64                     '!ref': outputPos[0] + ':' + outputPos[outputPos.length - 1] //设置填充区域
65                 }
66             )
67         }
68     };
69     var buffer = XLSX.write(wb, { bookType: type, bookSST: false, type: 'buffer' });
70 
71     writeFile(fileName + "." + type, buffer);
72 }
xlsx-util.js

 

 欢迎大家在评论区指正,不吝赐教!!!

 

posted @ 2019-02-01 12:56  叶枫声  阅读(26954)  评论(2编辑  收藏  举报