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

从服务端生成 Excel 电子表格

  •  
  •   GrapeCityChina · 2022-02-07 16:30:24 +08:00 · 645 次点击
    这是一个创建于 816 天前的主题,其中的信息可能已经有所发展或是发生改变。

    Node.js 是一个基于 Chrome V8 引擎的 JavaScript 运行环境,通常用于创建网络应用程序。它可以同时处理多个连接,并且不像其他大多数模型那样依赖线程。

    对于 Web 开发者来说,从数据库或 Web 服务器获取数据,然后输出到 Excel 文件以进行进一步分析的场景时有发生。我们的技术团队在跟国内外各行各业用户交流的过程中,就曾发现有很多的用户尝试在 Node.js 的环境下运行SpreadJS 纯前端表格控件,借助该控件,可以在服务器不预装任何 Excel 依赖项的情况下,收集用户输入的信息,并将其自动导出到 Excel 文件中。

    为了满足广大技术爱好者的需要,同时减少大家在未来技术选型方面所走的弯路,本文将就 SpreadJS 与 Node.js 之间的技术性方案进行探讨!

    一、安装 SpreadJS 和 Node .js

    首先,我们需要安装 Node.js 以及 Mock-Browser ,BufferJS 和 FileReader ,大家可以前往以下链接进行下载,同步操作:

    我们将使用 Visual Studio 创建应用程序。打开 Visual Studio 后,使用 JavaScript> Node.js>Blank Node.js 控制台应用程序模板创建一个新应用程序。这将自动创建所需的文件并打开" app.js"文件,也是我们将要更改的唯一文件。

    对于 BufferJS 库,您需要下载该软件包,然后通过导航到项目文件夹(一旦创建)并运行以下命令,将其手动安装到项目中:

    npm install

    安装完成后,您可能需要打开项目的 package.json 文件并将其添加到" dependencies"部分。文件内容应如下所示:

    {
    "name": "spread-sheets-node-jsapp",
    "version": "0.0.0",
    "description": "SpreadSheetsNodeJSApp",
    "main": "app.js",
    "author": {
       "name": "admin"
    },
    "dependencies": {
       "FileReader": "^0.10.2",
       "bufferjs": "1.0.0",
       "mock-browser": "^0.92.14"
      }
    }
    
    

    在此示例中,我们将使用 Node.js 的文件系统模块。我们可以将其加载到:

    var fs = require('fs')
    

    为了将 SpreadJS 与 Node.js 结合使用,我们还需要加载已安装的 Mock-Browser:

    var mockBrowser =require('mock-browser').mocks.MockBrowser
    

    在加载 SpreadJS 脚本之前,我们需要初始化模拟浏览器。初始化我们稍后在应用程序中可能需要使用的变量,尤其是" window"变量:

    global.window =mockBrowser.createWindow()
    global.document = window.document
    global.navigator = window.navigator
    global.HTMLCollection =window.HTMLCollection
    global.getComputedStyle =window.getComputedStyle
    
    

    初始化 FileReader 库:

    var fileReader = require('filereader');
    global.FileReader = fileReader;
    
    

    二、使用 SpreadJS npm 包

    将 SpreadJS 安装文件中的 SpreadJS Sheets 和 ExcelIO 包添加到项目中。

    您可以通过右键单击解决方案资源管理器的" npm"部分并将它们添加到您的项目中,然后选择"安装新的 NPM 软件包"。您应该能够搜索" GrapeCity"并安装以下 2 个软件包:

    @grapecity/spread-sheets
    @grapectiy/spread-excelio
    
    

    将 SpreadJS npm 软件包添加到项目后,正确的依赖关系将被写入 package.json:

    1.	{
    2.	"name": "spread-sheets-node-jsapp",
    3.	"version": "0.0.0",
    4.	"description": "SpreadSheetsNodeJSApp",
    5.	"main": "app.js",
    6.	"author": {
    7.	   "name": "admin"
    8.	},
    9.	  "dependencies":{
    10.	   "@grapecity/spread-excelio": "^11.2.1",
    11.	   "@grapecity/spread-sheets": "^11.2.1",
    12.	   "FileReader": "^0.10.2",
    13.	   "bufferjs": "1.0.0",
    14.	   "mock-browser": "^0.92.14"
    15.	  }
    16.	}
    
    

    现在我们需要在 app.js 文件中引入它:

    var GC =require('@grapecity/spread-sheets')
    var GCExcel =require('@grapecity/spread-excelio');
    
    

    使用 npm 软件包时,还需要设置许可证密钥(点击此处,免费申请许可证密钥):

    GC.Spread.Sheets.LicenseKey ="<YOUR KEY HERE>"
    

    在这个特定的应用程序中,我们将向用户显示他们正在使用哪个版本的 SpreadJS 。为此,我们可以引入 package.json 文件,然后引用依赖项以获取版本号:

    var packageJson =require('./package.json')
    console.log('\n** Using Spreadjs Version"' + packageJson.dependencies["@grapecity/spread-sheets"] +'" **')
    
    

    三、将 Excel 文件加载到您的 Node.js 应用程序中

    点击此处,下载现成的Excel 模板文件,该文件包含了从用户那里获取数据。接下来,将数据放入文件中并导出。在这种情况下,文件是用户可以编辑的状态。

    初始化工作簿和 ExcelIO 变量:

    var wb = new GC.Spread.Sheets.Workbook();
    var excelIO = new GCExcel.IO();
    
    
    

    我们在读取文件时将代码包装在 try / catch 块中。然后,初始化变量" readline",让您读取用户输入到控制台的数据。接下来,我们将其存储到一个 JavaScript 数组中,以便轻松填写 Excel 文件:

    // Instantiate the spreadsheet and modifyit
    console.log('\nManipulatingSpreadsheet\n---');
    try {
       var file = fs.readFileSync('./content/billingInvoiceTemplate.xlsx');
       excelIO.open(file.buffer, (data) => {
           wb.fromJSON(data);
           const readline = require('readline');
           var invoice = {
                generalInfo: [],
                invoiceItems: [],
                companyDetails: []
           };
       });
    } catch (e) {
       console.error("** Error manipulating spreadsheet **");
       console.error(e);
    }
    
    
    

    四、收集用户输入信息

    上图显示了我们正在使用的 Excel 文件。我们可以在 excelio.open 调用中创建一个单独的函数,以在控制台中提示用户需要的每一项内容。我们也可以创建一个单独的数组,将数据保存到每个输入后,然后将其推送到我们创建的 invoice.generalInfo 数组中:

    fillGeneralInformation();
    function fillGeneralInformation() {
       console.log("-----------------------\nFill in InvoiceDetails\n-----------------------")
       const rl = readline.createInterface({
           input: process.stdin,
           output: process.stdout
       });
       var generalInfoArray = [];
       rl.question('Invoice Number: ', (answer) => {
           generalInfoArray.push(answer);
           rl.question('Invoice Date (dd Month Year): ', (answer) => {
               generalInfoArray.push(answer);
                rl.question('Payment Due Date (ddMonth Year): ', (answer) => {
                    generalInfoArray.push(answer);
                    rl.question('Customer Name: ',(answer) => {
                       generalInfoArray.push(answer);
                        rl.question('CustomerCompany Name: ', (answer) => {
                           generalInfoArray.push(answer);
                            rl.question('Customer Street Address:', (answer) => {
                               generalInfoArray.push(answer);
                               rl.question('Customer City, State, Zip (<City>, <State Abbr><Zip>): ', (answer) => {
                                    generalInfoArray.push(answer);
                                   rl.question('Invoice Company Name: ', (answer) => {
                                       generalInfoArray.push(answer);
                                       rl.question('Invoice Street Address: ', (answer) => {
                                           generalInfoArray.push(answer);
                                           rl.question('Invoice City, State, Zip (<City>, <State Abbr><Zip>): ', (answer) => {
                                                generalInfoArray.push(answer);
                                               rl.close();
                                               invoice.generalInfo.push({
                                                   "invoiceNumber": generalInfoArray[0],
                                                   "invoiceDate": generalInfoArray[1],
                                                   "paymentDueDate": generalInfoArray[2],
                                                   "customerName": generalInfoArray[3],
                                                   "customerCompanyName": generalInfoArray[4],
                                                   "customerStreetAddress": generalInfoArray[5],
                                                   "customerCityStateZip": generalInfoArray[6],
                                                   "invoiceCompanyName": generalInfoArray[7],
                                                   "invoiceStreetAddress": generalInfoArray[8],
                                                   "invoiceCityStateZip": generalInfoArray[9],
                                                });
                                               console.log("General Invoice Information Stored");
                                               fillCompanyDetails();
                                            });
                                        });
                                   });
                                });
                            });
                        });
                    });
                });
           });
       });
    }
    
    
    

    该函数被称为" fillCompanyDetails",目的是收集有关公司的信息以填充到工作簿的第二张表中:

    function fillCompanyDetails() {
       console.log("-----------------------\nFill in CompanyDetails\n-----------------------")
       const rl = readline.createInterface({
           input: process.stdin,
           output: process.stdout
       });
       var companyDetailsArray = []
       rl.question('Your Name: ', (answer) => {
           companyDetailsArray.push(answer);
           rl.question('Company Name: ', (answer) => {
                companyDetailsArray.push(answer);
                rl.question('Address Line 1: ',(answer) => {
                   companyDetailsArray.push(answer);
                    rl.question('Address Line 2: ',(answer) => {
                       companyDetailsArray.push(answer);
                        rl.question('Address Line3: ', (answer) => {
                           companyDetailsArray.push(answer);
                            rl.question('AddressLine 4: ', (answer) => {
                               companyDetailsArray.push(answer);
                               rl.question('Address Line 5: ', (answer) => {
                                   companyDetailsArray.push(answer);
                                   rl.question('Phone: ', (answer) => {
                                       companyDetailsArray.push(answer);
                                       rl.question('Facsimile: ', (answer) => {
                                           companyDetailsArray.push(answer);
                                            rl.question('Website: ', (answer)=> {
                                               companyDetailsArray.push(answer);
                                               rl.question('Email: ', (answer) => {
                                                    companyDetailsArray.push(answer);
                                                   rl.question('Currency Abbreviation: ', (answer) => {
                                                       companyDetailsArray.push(answer);
                                                        rl.question('Beneficiary: ',(answer) => {
                                                           companyDetailsArray.push(answer);
                                                           rl.question('Bank: ', (answer) => {
                                                                companyDetailsArray.push(answer);
                                                               rl.question('Bank Address: ', (answer) => {
                                                                   companyDetailsArray.push(answer);
                                                                   rl.question('Account Number: ', (answer) => {
                                                                       companyDetailsArray.push(answer);
                                                                        rl.question('RoutingNumber: ', (answer) => {
                                                                           companyDetailsArray.push(answer);
                                                                           rl.question('Make Checks Payable To: ', (answer) => {
                                                                               companyDetailsArray.push(answer);
                                                                                rl.close();
                                                                               invoice.companyDetails.push({
                                                                                   "yourName": companyDetailsArray[0],
                                                                                   "companyName": companyDetailsArray[1],
                                                                                   "addressLine1": companyDetailsArray[2],
                                                                                   "addressLine2": companyDetailsArray[3],
                                                                                   "addressLine3": companyDetailsArray[4],
                                                                                   "addressLine4": companyDetailsArray[5],
                                                                                   "addressLine5": companyDetailsArray[6],
                                                                                    "phone":companyDetailsArray[7],
                                                                                   "facsimile": companyDetailsArray[8],
                                                                                    "website":companyDetailsArray[9],
                                                                                   "email": companyDetailsArray[10],
                                                                                   "currencyAbbreviation":companyDetailsArray[11],
                                                                                   "beneficiary": companyDetailsArray[12],
                                                                                   "bank":companyDetailsArray[13],
                                                                                   "bankAddress": companyDetailsArray[14],
                                                                                   "accountNumber": companyDetailsArray[15],
                                                                                   "routingNumber": companyDetailsArray[16],
                                                                                   "payableTo": companyDetailsArray[17]
                                                                               });
                                                                               console.log("Invoice Company Information Stored");
                                                                                console.log("-----------------------\nFillin Invoice Items\n-----------------------")
                                                                               fillInvoiceItemsInformation();
                                                                            });
                                                                       });
                                                                   });
                                                               });
                                                           });
                                                       });
                                                   });
                                                });
                                            });
                                        });
                                    });
                                });
                            });
                        });
                    });
                });
           });
       });
    }
    
    
    
    
    
    

    现在我们已经有了用户的基本信息,我们可以集中精力收集单个项目,并另命名为" fillInvoiceItemsInformation"函数。在每个项目执行之前,我们会询问用户是否要添加一个项目。如果他们继续输入" y",那么我们将收集该项目的信息,然后再次询问直到他们键入" n":

    function fillInvoiceItemsInformation() {
       const rl = readline.createInterface({
           input: process.stdin,
           output: process.stdout
       });
       var invoiceItemArray = [];
       rl.question('Add item?(y/n): ', (answer) => {
           switch (answer) {
                case "y":
                   console.log("-----------------------\nEnter ItemInformation\n-----------------------");
                    rl.question('Quantity: ',(answer) => {
                       invoiceItemArray.push(answer);
                        rl.question('Details: ',(answer) => {
                           invoiceItemArray.push(answer);
                            rl.question('UnitPrice: ', (answer) => {
                               invoiceItemArray.push(answer);
                               invoice.invoiceItems.push({
                                   "quantity":invoiceItemArray[0],
                                   "details": invoiceItemArray[1],
                                   "unitPrice": invoiceItemArray[2]
                                });
                                console.log("ItemInformation Added");
                                rl.close();
                               fillInvoiceItemsInformation();
                            });
                        });
                    });
                    break;
                case "n":
                   rl.close();
                    return fillExcelFile();
                    break;
                default:
                    console.log("Incorrectoption, Please enter 'y' or 'n'.");
           }
       });
    }
    
    

    五、填入您的 Excel 文件

    在收集所有必需的用户信息后,我们可以将其填入到 Excel 文件中:

    function fillExcelFile() {
       console.log("-----------------------\nFilling in Excelfile\n-----------------------");
       fillBillingInfo();
       fillCompanySetup();
    }
    function fillBillingInfo() {
       var sheet = wb.getSheet(0);
       sheet.getCell(0, 2).value(invoice.generalInfo[0].invoiceNumber);
       sheet.getCell(1, 1).value(invoice.generalInfo[0].invoiceDate);
       sheet.getCell(2, 2).value(invoice.generalInfo[0].paymentDueDate);
       sheet.getCell(3, 1).value(invoice.generalInfo[0].customerName);
       sheet.getCell(4, 1).value(invoice.generalInfo[0].customerCompanyName);
       sheet.getCell(5, 1).value(invoice.generalInfo[0].customerStreetAddress);
       sheet.getCell(6, 1).value(invoice.generalInfo[0].customerCityStateZip);
       sheet.getCell(3, 3).value(invoice.generalInfo[0].invoiceCompanyName);
       sheet.getCell(4, 3).value(invoice.generalInfo[0].invoiceStreetAddress);
       sheet.getCell(5, 3).value(invoice.generalInfo[0].invoiceCityStateZip);
    }
    function fillCompanySetup() {
       var sheet = wb.getSheet(1);
       sheet.getCell(2, 2).value(invoice.companyDetails[0].yourName);
       sheet.getCell(3, 2).value(invoice.companyDetails[0].companyName);
       sheet.getCell(4, 2).value(invoice.companyDetails[0].addressLine1);
       sheet.getCell(5, 2).value(invoice.companyDetails[0].addressLine2);
       sheet.getCell(6, 2).value(invoice.companyDetails[0].addressLine3);
       sheet.getCell(7, 2).value(invoice.companyDetails[0].addressLine4);
       sheet.getCell(8, 2).value(invoice.companyDetails[0].addressLine5);
       sheet.getCell(9, 2).value(invoice.companyDetails[0].phone);
       sheet.getCell(10, 2).value(invoice.companyDetails[0].facsimile);
       sheet.getCell(11, 2).value(invoice.companyDetails[0].website);
       sheet.getCell(12, 2).value(invoice.companyDetails[0].email);
       sheet.getCell(13, 2).value(invoice.companyDetails[0].currencyAbbreviation);
       sheet.getCell(14, 2).value(invoice.companyDetails[0].beneficiary);
       sheet.getCell(15, 2).value(invoice.companyDetails[0].bank);
       sheet.getCell(16, 2).value(invoice.companyDetails[0].bankAddress);
       sheet.getCell(17, 2).value(invoice.companyDetails[0].accountNumber);
       sheet.getCell(18, 2).value(invoice.companyDetails[0].routingNumber);
       sheet.getCell(19, 2).value(invoice.companyDetails[0].payableTo);
    }
    
    
    

    为了防止用户添加的数量超过工作表最大行数,我们可以在工作表中自动添加更多行。在设置数组中表单中的项目之前,默认添加行:

    function fillInvoiceItems() {
       var sheet = wb.getSheet(0);
       var rowsToAdd = 0;
       if (invoice.invoiceItems.length > 15) {
           rowsToAdd = invoice.invoiceItems.length - 15;
           sheet.addRows(22, rowsToAdd);
       }
       var rowIndex = 8;
       if (invoice.invoiceItems.length >= 1) {
           for (var i = 0; i < invoice.invoiceItems.length; i++) {
                sheet.getCell(rowIndex,1).value(invoice.invoiceItems.quantity);
                sheet.getCell(rowIndex,2).value(invoice.invoiceItems.details);
                sheet.getCell(rowIndex,3).value(invoice.invoiceItems.unitPrice);
                rowIndex++;
           }
       }
    }
    
    
    

    六、将文档内容从 Node.js 导出到 Excel 文件

    在工作簿中填写完信息后,我们可以将工作簿导出到 Excel 文件中。为此,我们将使用 excelio 打开功能。在这种情况下,只需将日期输入文件名即可:

    function exportExcelFile() {
       excelIO.save(wb.toJSON(), (data) => {
           fs.appendFileSync('Invoice' + new Date().valueOf() + '.xlsx', newBuffer(data), function (err) {
                console.log(err);
           });
           console.log("Export success");
       }, (err) => {
           console.log(err);
       }, { useArrayBuffer: true });
    }
    
    
    

    完成的文件将如下所示:

    以上就是第一篇《从服务端生成 Excel 电子表格( Node.js+SpreadJS )》的全部内容。为了能够解决批量绑定数据源并导出 Excel 、批量修改大量 Excel 内容及样式、服务端批量打印以及生成 PDF 文档等需求,我们提供了更为成熟的官方手段:SpreadJS + GcExcel,该方案提供了比 Node.js+SpreadJS 更加优秀的性能和稳定性,这就是我们下一篇《从服务端生成 Excel 电子表格( GcExcel + SpreadJS )》的主要内容,敬请期待。

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