從服務端生成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)

完整的測試工程請參考:https://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請參考附件:https://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 javaGcExcel for .NET)來說顯然更加合適。

以上就是本篇的全部內容,結合本文的測試結果,對於批量處理、修改、導出Excel,以及服務端批量打印和生成PDF文檔的需求, SpreadJS + GcExcel都能提供更加優秀的性能和穩定性表現,可以放心將其作爲未來項目的首選方案。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章