Это пятая статья в серии, в которой мы научимся использовать мощную логику такого языка программирования, как JavaScript, для управления данными электронных таблиц.

В этой статье мы поговорим о том, как читать и анализировать файлы XSLX с помощью JavaScript в Node.js.

Теперь, когда вы понимаете, как Node.js можно использовать для чтения файлов на жестком диске и как вы можете создавать команды для интерфейса командной строки (CLI), давайте узнаем, как читать файлы, сохраненные Microsoft Excel.

Вы, безусловно, можете использовать fs.readFile() для чтения файла XLSX и написания кода для анализа данных, найденных в нем. Однако это еще один случай, когда очень умные люди уже проделали эту сложную работу и создали на ее основе пакет npm.

Пакет, который мы будем использовать, называется ExcelJS. Установите его сейчас.

npm i exceljs

Требовать это вверху index.js

const exceljs = require("exceljs");

Под нашей первой командой добавьте еще одну.

// A Commander command for listing first column of first sheet
commander.program
  .command("listFirstCol <path>")
  .description("List first sheet column A")
  .action(listFirstColAction);

action здесь будет вызывать listFirstColAction(), которого еще не существует. Идите и создайте это.

// Function for reading XLSX, logging first column of first sheet
function listFirstColAction(path) {
    // List logic here
}

Читая документацию по ExcelJS, я вижу, что чтение файлов XLSX происходит так:

// Create an instance of workbook to load data into
const workbook = new exceljs.Workbook();
// Read the file
workbook.xlsx.readFile(path) {
    .then(function(book) {
        // Do something with the workbook
    });
}

Если у вас есть ссылка на книгу в переменной book, вы можете получить конкретный лист.

// Get reference to first worksheet
const sheet = book.getWorksheet(1);

И, наконец, вы можете записать значения первого столбца в консоль.

// Log the values of first column
console.log(sheet.getColumn(1).values);

Вы можете заметить, что некоторые части ExcelJS индексируются по одному. В этом отличие от нативного JavaScript, который имеет нулевую индексацию. Первый лист имеет индекс 1. Первый столбец имеет индекс 1.

Вся функция listFirstColAction() выглядит так:

// Function for reading XLSX, logging first column of first sheet
function listFirstColAction(path) {
    // Create an instance of workbook to load data into
    const workbook = new exceljs.Workbook();
// Read the file
    workbook.xlsx.readFile(path)
        .then(function(book) {
            // Get reference to first worksheet
            const sheet = book.getWorksheet(1);
            // Log the values of first column
            console.log(sheet.getColumn(1).values);
        });
}

Если вам интересно, почему мы должны использовать .then() после чтения файла, это просто соглашение, которое использует ExcelJS. Это популярный шаблон, который называется "обещания", и он является альтернативой шаблону обратного вызова, который мы использовали ранее. Не все библиотеки JavaScript используют обещания, и не все также используют обратные вызовы. Однако некоторые даже поддерживают и то, и другое!

В любом случае выполнение этой команды дает ожидаемый результат.

Вот как выглядит полный index.js. Он становится приличного размера.

// Require outside packages
const fs = require("fs");
const commander = require("commander");
const exceljs = require("exceljs");
// Function for taking a file path and logging the contents
function logAction(path) {
    // Used as a callback when a file is read
    function readFileCallback(err, data) {
        if (err !== null) {
            switch (err.code) {
                case "ENOENT":
                    console.error("That file doesn't exist.");
                    break;
                default:
                    console.error(err.message);
            }
        } else {
            // Convert the file's data buffer to a string
            const fileContents = data.toString();
            // Log the file contents
            console.log(fileContents);
        }
    }
    // Read the file at the path
    fs.readFile(path, "", readFileCallback);
}
// Function for reading XLSX, logging first column of first sheet
function listFirstColAction(path) {
    // Create an instance of workbook to load data into
    const workbook = new exceljs.Workbook();
    // Read the file
    workbook.xlsx.readFile(path)
        .then(function(book) {
            // Get reference to first worksheet
            const sheet = book.getWorksheet(1);
            // Log the values of first column
            console.log(sheet.getColumn(1).values);
        });
}
// A Commander command for reading files and logging the contents
commander.program
  .command("log <path>")
  .description("Log a text file to the console")
  .action(logAction);
// A Commander command for listing first column of first sheet
commander.program
  .command("listFirstCol <path>")
  .description("List first sheet column A")
  .action(listFirstColAction);
// Parse the CLI arguments with Commander
commander.program.parse(process.argv);

Это более 60 строк кода, включая комментарии. Несмотря на то, что файлы исходного кода обычно превышают это количество строк, я все же вижу некоторую ценность в разделении нашего кода на логические разделы в настоящее время.

logAction() и listFirstColAction() можно переместить в свои файлы.

Создайте новую папку с именем actions. Внутри него создайте два файла с именами log и listFirstcol.

Вырежьте всю функцию logAction() и вставьте ее в actions/log.js.

Нам нужно указать Node.js «экспортировать», что эту функцию следует «экспортировать» из файла каждый раз, когда она «требуется» в другом файле. Просто добавьте module.exports = перед определением функции.

module.exports = function logAction(path) {
    // ... snip ...
};

Поскольку эта функция использует fs, мы должны убедиться, что она требуется в верхней части этого файла. Добавьте требование вверху файла.

// Require outside packages
const fs = require("fs");

Весь файл actions/log.js выглядит так:

// Require outside packages
const fs = require("fs");
// Function for taking a file path and logging the contents
module.exports = function logAction(path) {
    // Used as a callback when a file is read
    function readFileCallback(err, data) {
        if (err !== null) {
            switch (err.code) {
                case "ENOENT":
                    console.error("That file doesn't exist.");
                    break;
                default:
                    console.error(err.message);
            }
        } else {
            // Convert the file's data buffer to a string
            const fileContents = data.toString();
            // Log the file contents
            console.log(fileContents);
        }
    }
// Read the file at the path
    fs.readFile(path, "", readFileCallback);
};

Повторите этот же процесс для actions/listFirstCol.js, помня, что он не использует fs пакет, но использует exceljs.

// Require outside packages
const exceljs = require("exceljs");
// Function for reading XLSX, logging first column of first sheet
module.exports = function listFirstColAction(path) {
    // Create an instance of workbook to load data into
    const workbook = new exceljs.Workbook();
    // Read the file
    workbook.xlsx.readFile(path)
        .then(function(book) {
            // Get reference to first worksheet
            const sheet = book.getWorksheet(1);
            // Log the values of first column
            console.log(sheet.getColumn(1).values);
        });
};

Теперь, когда index.js не использует ни fs, ни exceljs, вы можете удалить для них require строки.

Поскольку мы переместили каждое из наших двух действий в отдельные файлы, index.js теперь намного меньше.

// Require outside packages
const commander = require("commander");
// A Commander command for reading files and logging the contents
commander.program
  .command("log <path>")
  .description("Log a text file to the console")
  .action(logAction);
// A Commander command for listing first column of first sheet
commander.program
  .command("listFirstCol <path>")
  .description("List first sheet column A")
  .action(listFirstColAction);
// Parse the CLI arguments with Commander
commander.program.parse(process.argv);

Но если вы попытаетесь запустить программу, вы увидите ошибку.

logAction используется в нашей программе, но нигде не определен. Верно, потому что мы его переместили.

Мы можем просто require это из сохраненного файла. Сделайте это для обоих действий.

// Require outside packages
const commander = require("commander");
const logAction = require("./actions/log");
const listFirstColAction = require("./actions/listFirstCol");

Сохраните файл, попробуйте еще раз, и он работает!

В этой статье мы узнали, как читать и анализировать файлы XLSX с помощью ExcelJS. Пока что мы использовали лишь очень небольшой набор функций из этого пакета. Вы можете подумать о том, чтобы прочитать документацию и увидеть все, что умеет ExcelJS.

В следующей статье мы попытаемся сгенерировать некоторые сводные данные из нашей электронной таблицы.