從服務端生成Excel電子表格(GcExcel + SpreadJS)
在服務端生成Excel電子表格,除了使用 Node.js + SpreadJS 外,葡萄城官方推薦使用 SpreadJS + GcExcel。該方案不僅能夠解決批量繫結資料來源並匯出Excel、批量修改大量Excel內容及樣式、服務端批量列印以及生成PDF文件等需求,還提供了遠超行業標準的元件效能。
為了驗證SpreadJS + GcExcel的處理效能,本文將就 GcExcel for Java 和Node.js中執行SpreadJS的各項資料進行對比。由於SpreadJS和GcExcel的元件功能非常豐富,本文僅選擇最為常見的兩個功能點做對比,分別是設定區域資料和匯出Excel文件。
一、本次測試的幾個大前提
由於Node.js是基於V8引擎來執行JavaScript的,因此它的js也是基於事件機制的非阻塞單執行緒執行,其檔案的I/O都是非同步執行的,而Node.js之所以選擇單執行緒的方式是因為編碼簡單、開發難度低、對"碼農"碼農的心智消耗相對較小;而且它的檔案I/O是非同步執行的,所以不需要像Java那樣需要建立、回收執行緒(Node.js的I/O操作在底層也是執行緒,這裡不做深入討論),這方面開銷較小。
但是,單執行緒在做複雜運算方面相比多執行緒則沒有任何優勢,也無法利用多執行緒來有效調配多核CPU進行優化,因此在Node.js中執行SpreadJS就只能是單執行緒JS,這也會影響SpreadJS 的資料處理效能。
所以,為了獲得更加準確的測試結果,本篇中設計的測試用例,在兩個環境(Java 和 Node.js)中都採用單執行緒執行,並且選擇了與Node.js更加匹配的批量I/O操作作為測試用例。
二、 Node.js 與 SpreadJS 的測試程式碼和結果:
軟體版本 | CPU | 記憶體 |
---|---|---|
Node.js 16.10.0 | Intel(R) Core(TM) i7-9750HQ CPU @ 2.80 GHz | 32G |
測試程式碼:如下所示,用一個Performance類執行1000次設定資料、匯出Excel文件的操作。
const fs = require('fs'); // Initialize the mock browser variables const mockBrowser = require('mock-browser').mocks.MockBrowser; global.window = mockBrowser.createWindow(); global.document = window.document; global.navigator = window.navigator; global.HTMLCollection = window.HTMLCollection; global.getComputedStyle = window.getComputedStyle; const fileReader = require('filereader'); global.FileReader = fileReader; const GC = require('@grapecity/spread-sheets'); const GCExcel = require('@grapecity/spread-excelio'); GC.Spread.Sheets.LicenseKey = GCExcel.LicenseKey = "Your License"; const dataSource = require('./data'); function runPerformance(times) { const timer = `test in ${times} times`; console.time(timer); for(let t=0; t<times; t++) { // const hostDiv = document.createElement('div'); // hostDiv.id = 'ss'; // document.body.appendChild(hostDiv); const wb = new GC.Spread.Sheets.Workbook()//global.document.getElementById('ss')); const sheet = wb.getSheet(0); for(let i=0; i<dataSource.length; i++) { sheet.setValue(i, 0, dataSource[i]["Film"]); sheet.setValue(i, 1, dataSource[i]["Genre"]); sheet.setValue(i, 2, dataSource[i]["Lead Studio"]); sheet.setValue(i, 3, dataSource[i]["Audience Score %"]); sheet.setValue(i, 4, dataSource[i]["Profitability"]); sheet.setValue(i, 5, dataSource[i]["Rating"]); sheet.setValue(i, 6, dataSource[i]["Worldwide Gross"]); sheet.setValue(i, 7, dataSource[i]["Year"]); } exportExcelFile(wb, times, t); } } function exportExcelFile(wb, times, t) { const excelIO = new GCExcel.IO(); excelIO.save(wb.toJSON(), (data) => { fs.appendFile('results/Invoice' + new Date().valueOf() + '_' + t + '.xlsx', new Buffer(data), function (err) { if (err) { console.log(err); }else { if(t === times-1) { console.log('Export success'); console.timeEnd(`test in ${times} times`); } } }); }, (err) => { console.log(err); }, { useArrayBuffer: true }); } runPerformance(1000)
完整的測試工程請參考: http://gitee.com/GrapeCity/Node.js-SpreadJS-two.git
測試工程執行方式:
- npm install
- node ./app.js
執行結果:平均每次花費 18.1 ms
三、 GcExcel 的測試程式碼和結果
軟體版本 | CPU | 記憶體 |
---|---|---|
GcExcel V5.0 | Intel(R) Core(TM) i7-9750HQ CPU @ 2.80 GHz | 32G |
測試程式碼如下所示:
public class Performance { public static void main(String[] args) { System.out.println(System.getProperty("user.dir") + "/sources/jsonData"); String jsonStr = readTxtFileIntoStringArrList(System.getProperty("user.dir") + "/sources/jsonData"); JSONArray jsonArr = JSON.parseArray(jsonStr); //JSONObject jsonObj = (JSONObject) jsonArr.get(0); //System.out.println(jsonObj.get("Film")); run(1000, jsonArr); } public static void run(int times, JSONArray dataArr) { String path = System.getProperty("user.dir") + "/results/"; System.out.println(path + "result.xlsx"); long start = new Date().getTime(); for (int i = 0; i < times; i++) { Workbook workbook = new Workbook(); IWorksheet worksheet = workbook.getWorksheets().get(0); for (int j = 0; j < dataArr.size(); j++) { JSONObject jsonObj = (JSONObject) dataArr.get(j); worksheet.getRange(j, 0, 1, 8).get(0).setValue(jsonObj.get("Film")); worksheet.getRange(j, 0, 1, 8).get(1).setValue(jsonObj.get("Genre")); worksheet.getRange(j, 0, 1, 8).get(2).setValue(jsonObj.get("Lead Studio")); worksheet.getRange(j, 0, 1, 8).get(3).setValue(jsonObj.get("Audience Score %")); worksheet.getRange(j, 0, 1, 8).get(4).setValue(jsonObj.get("Profitability")); worksheet.getRange(j, 0, 1, 8).get(5).setValue(jsonObj.get("Rating")); worksheet.getRange(j, 0, 1, 8).get(6).setValue(jsonObj.get("Worldwide Gross")); worksheet.getRange(j, 0, 1, 8).get(7).setValue(jsonObj.get("Year")); } workbook.save(path + "result" + i + ".xlsx"); } System.out.println("執行"+times+"次花費時常(ms): " + (new Date().getTime() - start)); } public static String readTxtFileIntoStringArrList(String filePath) { StringBuilder list = new StringBuilder(); try { String encoding = "GBK"; File file = new File(filePath); if (file.isFile() && file.exists()) { InputStreamReader read = new InputStreamReader(new FileInputStream(file), encoding);// 考慮到編碼格式 BufferedReader bufferedReader = new BufferedReader(read); String lineTxt = null; while ((lineTxt = bufferedReader.readLine()) != null) { list.append(lineTxt); } bufferedReader.close(); read.close(); } else { System.out.println("找不到指定的檔案"); } } catch (Exception e) { System.out.println("讀取檔案內容出錯"); e.printStackTrace(); } return list.toString(); } }
完整的測試工程zip請參考附件: http://gitee.com/GrapeCity/Node.js-SpreadJS-two.git
測試指令碼執行方式:匯入Eclipse後直接run as Application
執行結果如下所示:平均每次花費 8.4 ms
四、總結分析:
1 、測試結果分析:node.js平均每次花費 18.1 ms,GcExcel平均每次花費 8.4 ms,兩者同時執行1000次設定資料、匯出Excel文件的操作,效能相差2倍。
2、處理效能的對比分析:
即便對於單執行緒的批量I/O操作,SpreadJS 在 Node.js的執行效能仍不如SpreadJS 在GcExcel for Java中執行,一方面是由於GcExcel效能的確非常優秀,它在Java平臺上運用了很多優秀、成熟的解決方案,做到了同類產品中最一流的效能表現,另一方面是由於GcExcel對Excel和SpreadJS有更加全面的功能支援。目前,GcExcel已經作為行業內伺服器端處理Excel文件的首選方案。
3 、技術選型的分析:
除了效能、編碼難度外,對於技術選型而言,有一點也不容忽視,即平臺。如果專案本身採用的是Java Web或 .Net Web架構,那麼對於提供雙平臺支援的GcExcel( GcExcel for java 和 GcExcel for .NET )來說顯然更加合適。
以上就是本篇的全部內容,結合本文的測試結果,對於批量處理、修改、匯出Excel,以及服務端批量列印和生成PDF文件的需求, SpreadJS + GcExcel 都能提供更加優秀的效能和穩定性表現,可以放心將其作為未來專案的首選方案。
- 【演算法篇】刷了兩道大廠面試題,含淚 ”重學陣列“
- 2022 開源軟體安全狀況報告:超 41% 的企業對開源安全沒有足夠的信心
- JavaScript中async和await的使用以及佇列問題
- Flex & Bison 開始
- Obsidian基礎教程
- 分享自己平時使用的socket多客戶端通訊的程式碼技術點和軟體使用
- iNeuOS工業網際網路作業系統,增加2154個檢視建模(WEB組態)行業向量圖元、大屏背景及相關圖元
- 多臺雲伺服器的 Kubernetes 叢集搭建
- Elasticsearch學習系列四(聚合搜尋)
- 關於swiper外掛在vue2的使用
- 使用 Abp.Zero 搭建第三方登入模組(一):原理篇
- LVGL庫入門教程 - 顏色和影象
- Node.js精進(4)——事件觸發器
- 物聯網?快來看 Arduino 上雲啦
- SpringBoot JWT Redis 開源知識社群系統
- CVPR2022 | 可精簡域適應
- Spring框架系列(3) - 深入淺出Spring核心之控制反轉(IOC)
- 面試突擊59:一個表中可以有多個自增列嗎?
- CVPR2022 | 弱監督多標籤分類中的損失問題
- JDBC、ORM、JPA、Spring Data JPA,傻傻分不清楚?一文帶你釐清箇中曲直,給你個選擇SpringDataJPA的...