V2EX = way to explore
V2EX 是一个关于分享和探索的地方
现在注册
已注册用户请  登录
GrapeCityChina
V2EX  ›  推广

从服务端生成 Excel 电子表格(GcExcel + SpreadJS)

  •  
  •   GrapeCityChina · 2022-02-09 13:49:11 +08:00 · 574 次点击
    这是一个创建于 1019 天前的主题,其中的信息可能已经有所发展或是发生改变。

    在服务端生成 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都能提供更加优秀的性能和稳定性表现,可以放心将其作为未来项目的首选方案。

    目前尚无回复
    关于   ·   帮助文档   ·   博客   ·   API   ·   FAQ   ·   实用小工具   ·   1387 人在线   最高记录 6679   ·     Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 · 24ms · UTC 17:15 · PVG 01:15 · LAX 09:15 · JFK 12:15
    Developed with CodeLauncher
    ♥ Do have faith in what you're doing.