AutomationFlowsGeneral › Merge Multiple Excel Files Into a Single Multi-sheet File with Summary Page

Merge Multiple Excel Files Into a Single Multi-sheet File with Summary Page

BySimone @c1499 on n8n.io

This workflow automates the process of merging multiple files from a designated folder into a single, well-organized Excel workbook. Each input file is converted into its own sheet within the output file. Additionally, a summary sheet is generated at the beginning, providing a…

Event trigger★★★★☆ complexity11 nodesRead Write File
General Trigger: Event Nodes: 11 Complexity: ★★★★☆ Added:

This workflow corresponds to n8n.io template #8373 — we link there as the canonical source.

The workflow JSON

Copy or download the full n8n JSON below. Paste it into a new n8n workflow, add your credentials, activate. Full import guide →

Download .json
{
  "nodes": [
    {
      "id": "b0865f1f-7e49-48af-829c-49202779ff8e",
      "name": "When clicking \u2018Execute workflow\u2019",
      "type": "n8n-nodes-base.manualTrigger",
      "position": [
        -944,
        -16
      ],
      "parameters": {},
      "typeVersion": 1
    },
    {
      "id": "a4eb6e0e-999e-4056-9421-abac99c18df1",
      "name": "Read each XLXS",
      "type": "n8n-nodes-base.splitInBatches",
      "position": [
        -496,
        -16
      ],
      "parameters": {
        "options": {}
      },
      "typeVersion": 3
    },
    {
      "id": "ee1d615e-5e7c-4696-b633-113b323536dc",
      "name": "Read XLXS Files from Disk",
      "type": "n8n-nodes-base.readWriteFile",
      "position": [
        -720,
        -16
      ],
      "parameters": {
        "options": {},
        "fileSelector": "n8n_files/*.xlsx"
      },
      "typeVersion": 1
    },
    {
      "id": "8d2bf3e9-2d8a-4279-90e0-3a1c8b49b683",
      "name": "Create Multi-Sheet Excel",
      "type": "n8n-nodes-base.code",
      "position": [
        -32,
        -224
      ],
      "parameters": {
        "jsCode": "// Use the XLSX library to create a multi-sheet Excel file\nconst XLSX = require('xlsx');\n\n// Get all file data\nconst allFiles = $input.first().json.allFiles;\n\nif (!allFiles || allFiles.length === 0) {\n  throw new Error('No files to process');\n}\n\n// Create a new workbook\nconst workbook = XLSX.utils.book_new();\n\n// Create a worksheet for each file\nallFiles.forEach((file, index) => {\n  console.log(`Creating sheet: ${file.sheetName}`);\n  \n  let sheetName = file.sheetName;\n  \n  // Ensure the sheet name is unique and conforms to Excel standards\n  if (workbook.SheetNames.includes(sheetName)) {\n    sheetName = `${sheetName}_${index + 1}`;\n  }\n  \n  // Excel sheet name limitations: max 31 characters, cannot contain special characters\n  sheetName = sheetName\n    .replace(/[\\[\\]\\*\\/\\\\\\?\\:]/g, '_')\n    .substring(0, 31);\n  \n  // Create the worksheet\n  let worksheet;\n  \n  if (file.data && file.data.length > 0) {\n    // Convert JSON data to a worksheet\n    worksheet = XLSX.utils.json_to_sheet(file.data);\n    \n    // To add metadata, we need to recreate the worksheet\n    const wsData = XLSX.utils.sheet_to_json(worksheet, { header: 1 });\n    const finalData = [\n      ...wsData\n    ];\n    \n    worksheet = XLSX.utils.aoa_to_sheet(finalData);\n  } else {\n    // If there is no data, create an empty worksheet\n    worksheet = XLSX.utils.aoa_to_sheet([\n      [`Original File: ${file.originalFileName}`],\n      ['No data'],\n      [`Processing Time: ${new Date().toLocaleString()}`]\n    ]);\n  }\n  \n  // Add the worksheet to the workbook\n  XLSX.utils.book_append_sheet(workbook, worksheet, sheetName);\n  \n  console.log(`Sheet '${sheetName}' created with ${file.recordCount} records`);\n});\n\n// Create a summary sheet\nconst summaryData = [\n  ['File Summary Report'],\n  ['Generation Time', new Date().toLocaleString()],\n  ['Total Files', allFiles.length],\n  [],\n  ['Sheet Name', 'Original File Name', 'Record Count']\n];\n\nallFiles.forEach(file => {\n  summaryData.push([file.sheetName, file.originalFileName, file.recordCount]);\n});\n\nconst summarySheet = XLSX.utils.aoa_to_sheet(summaryData);\nXLSX.utils.book_append_sheet(workbook, summarySheet, 'Summary');\n\n// Convert the workbook to a buffer\nconst excelBuffer = XLSX.write(workbook, { \n  type: 'buffer', \n  bookType: 'xlsx',\n  compression: true\n});\n\n// Create a file name (including a timestamp)\nconst timestamp = new Date().toISOString().replace(/[:\\-T]/g, '').split('.')[0];\nconst fileName = `Merged_Files_${timestamp}.xlsx`;\n\nconsole.log(`Excel file created: ${fileName}`);\nconsole.log(`Total sheets: ${workbook.SheetNames.length}`);\nconsole.log(`Sheet names: ${workbook.SheetNames.join(', ')}`);\n\n// Return binary data\n// Convert buffer to base64 so n8n can download it\nconst base64Data = excelBuffer.toString('base64');\n\nreturn [{\n  json: {\n    fileName: fileName,\n    sheetsCreated: workbook.SheetNames,\n    totalFiles: allFiles.length,\n    summary: `Successfully merged ${allFiles.length} Excel files into ${workbook.SheetNames.length} worksheets`\n  },\n  binary: {\n    data: {\n      data: base64Data,\n      mimeType: 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet',\n      fileName: fileName\n    }\n  }\n}];\n"
      },
      "executeOnce": true,
      "typeVersion": 2,
      "alwaysOutputData": false
    },
    {
      "id": "3371ae1b-7fc2-4981-9893-a08760e1549d",
      "name": "Collect and Process Data",
      "type": "n8n-nodes-base.code",
      "position": [
        -240,
        -224
      ],
      "parameters": {
        "jsCode": "const allFiles = [];\n\nfor (const item of $input.all()) {\n  // First, get the file name (prioritizing from json, then from binary metadata)\n  const fileName =\n    item.json.fileName ??\n    item.binary?.data?.fileName ??\n    'UnknownFile';\n\n  const sheetName = fileName\n    .replace(/^.*[\\/\\\\]/, '')\n    .replace(/\\.[^/.]+$/, '');\n\n  // 'Extract from File' puts an array of \"rows\" into json.data\n  let sheetData = item.json.data ?? [];\n  if (!Array.isArray(sheetData)) sheetData = [sheetData];\n\n  // Filter out empty rows\n  const cleanedData = sheetData.filter(row =>\n    row && typeof row === 'object' &&\n    Object.values(row).some(v => v !== null && v !== undefined && v !== '')\n  );\n\n  allFiles.push({\n    sheetName,\n    data: cleanedData,\n    originalFileName: fileName,\n    recordCount: cleanedData.length,\n  });\n}\n\nreturn [{ json: { allFiles } }];\n"
      },
      "typeVersion": 2
    },
    {
      "id": "generated-4d48e4e6-790e-4517-aecb-695cb18ceca8",
      "name": "\u5de5\u4f5c\u6d41\u542f\u52a8\u4e0e\u6587\u4ef6\u8bfb\u53d6",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        -1024,
        -208
      ],
      "parameters": {
        "color": 5,
        "width": 680,
        "height": 554,
        "content": "## \ud83d\ude80 1. Workflow Initiation and File Reading\r\r- **When clicking \u2018Execute workflow\u2019**: Manually trigger the workflow.\r- **Read XLXS Files from Disk**: Read all `.xlsx` formatted files from the `n8n_files/` directory mounted to the container.\r- **Read each XLXS**: Split all found files into batches, processing one file at a time to extract data individually."
      }
    },
    {
      "id": "generated-7a99b3f6-0daf-4d46-8d17-e9e5cf96472e",
      "name": "\u6570\u636e\u63d0\u53d6\u4e0e\u5904\u7406",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        -288,
        0
      ],
      "parameters": {
        "color": 4,
        "width": 704,
        "height": 346,
        "content": "## \ud83d\udcca 2. Data Extraction and Processing\r\r- **Extract from XLSX**: Extract data from a single XLSX file.\r- **Aggregate**: This node is used to summarize the read JSON data into one."
      }
    },
    {
      "id": "generated-998281e0-d0e4-4c79-af6f-c174dd8cf857",
      "name": "\u751f\u6210\u4e0e\u4fdd\u5b58Excel\u6587\u4ef6",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        -288,
        -736
      ],
      "parameters": {
        "width": 706,
        "height": 720,
        "content": "## \ud83d\udcdd 3. Generate and Save Multi-Worksheet Excel File\r\r- **Collect and Process Data**: This code node collects data from all files, cleans up empty rows, and prepares an object for each file containing worksheet names, data, and metadata.\r- **Create Multi-Sheet Excel**: This code node merges all processed data into a new Excel file, with each original file corresponding to a worksheet, and additionally creates a summary sheet.\r    - **\ud83d\udd27 Tip:** You need to enable the external module `xlsx` in your n8n environment variables to run it properly.\r- Modify your **docker-compose.xml** file:\r         - Set `NODE_FUNCTION_ALLOW_EXTERNAL=xlsx` in the configuration file (e.g., `docker-compose.yml` or `.env` file).\r         - Delete **\u201cimage: n8nio/n8n:latest\u201d** and replace it with **\u201cbuild: .\u201d**\r      - Please create a Dockerfile file and enter the following content:\r```\r      FROM n8nio/n8n:latest\r      USER root\r      RUN npm install xlsx\rENV NODE_FUNCTION_ALLOW_EXTERNAL=xlsx\r      ENV NODE_PATH=/home/node/node_modules\r      USER node\r```\r\r- **Write Files from Disk**: Save the final generated Excel files to the `n8n_files/output/` directory."
      }
    },
    {
      "id": "88841064-e087-47f9-97bc-8eb94635d651",
      "name": "Save XLXS to Disk",
      "type": "n8n-nodes-base.readWriteFile",
      "position": [
        176,
        -224
      ],
      "parameters": {
        "options": {},
        "fileName": "=n8n_files/output/{{$json.fileName}}",
        "operation": "write"
      },
      "typeVersion": 1
    },
    {
      "id": "448417f6-a9dc-4fc2-a05c-1b53da843f1f",
      "name": "XLSX to Json List",
      "type": "n8n-nodes-base.extractFromFile",
      "position": [
        -176,
        160
      ],
      "parameters": {
        "options": {
          "rawData": true,
          "headerRow": true,
          "includeEmptyCells": false
        },
        "operation": "xlsx"
      },
      "typeVersion": 1
    },
    {
      "id": "e4354d1b-87fb-4e21-869d-7706acef1f76",
      "name": "Mulipte Json to Single Json",
      "type": "n8n-nodes-base.aggregate",
      "position": [
        64,
        160
      ],
      "parameters": {
        "options": {},
        "aggregate": "aggregateAllItemData"
      },
      "typeVersion": 1
    }
  ],
  "connections": {
    "Read each XLXS": {
      "main": [
        [
          {
            "node": "Collect and Process Data",
            "type": "main",
            "index": 0
          }
        ],
        [
          {
            "node": "XLSX to Json List",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "XLSX to Json List": {
      "main": [
        [
          {
            "node": "Mulipte Json to Single Json",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Collect and Process Data": {
      "main": [
        [
          {
            "node": "Create Multi-Sheet Excel",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Create Multi-Sheet Excel": {
      "main": [
        [
          {
            "node": "Save XLXS to Disk",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Read XLXS Files from Disk": {
      "main": [
        [
          {
            "node": "Read each XLXS",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Mulipte Json to Single Json": {
      "main": [
        [
          {
            "node": "Read each XLXS",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "When clicking \u2018Execute workflow\u2019": {
      "main": [
        [
          {
            "node": "Read XLXS Files from Disk",
            "type": "main",
            "index": 0
          }
        ]
      ]
    }
  }
}
Pro

For the full experience including quality scoring and batch install features for each workflow upgrade to Pro

About this workflow

This workflow automates the process of merging multiple files from a designated folder into a single, well-organized Excel workbook. Each input file is converted into its own sheet within the output file. Additionally, a summary sheet is generated at the beginning, providing a…

Source: https://n8n.io/workflows/8373/ — original creator credit. Request a take-down →

More General workflows → · Browse all categories →

Related workflows

Workflows that share integrations, category, or trigger type with this one. All free to copy and import.

General

This workflow automates the backup of decrypted n8n credentials from a self-hosted Docker instance to Google Drive. It allows you to export credentials on n8n versions 2.x.x (where old CLI commands ma

Ssh, Read Write File, Google Drive
General

Convert Docx From Url To Pdf Using Convertapi. Uses manualTrigger, httpRequest, readWriteFile, stickyNote. Event-driven trigger; 6 nodes.

HTTP Request, Read Write File
General

Convert Xlsx To Pdf Using Convertapi. Uses manualTrigger, readWriteFile, stickyNote, httpRequest. Event-driven trigger; 5 nodes.

Read Write File, HTTP Request
General

Convert Pptx To Pdf Using Convertapi. Uses manualTrigger, readWriteFile, stickyNote, httpRequest. Event-driven trigger; 5 nodes.

Read Write File, HTTP Request
General

Convert Pdf To Pdfa Using Convertapi. Uses manualTrigger, readWriteFile, stickyNote, httpRequest. Event-driven trigger; 5 nodes.

Read Write File, HTTP Request