---
title: "Stream a large XLSX file in C#"
url: https://recursiveai.org/onedocument/cookbook/stream-large-excel-file/
description: "Read an XLSX worksheet one row at a time without loading the workbook model."
version: "1.0.0"
---

Read an XLSX worksheet one row at a time without loading the workbook model.

```shell
dotnet add package \
  RecursiveAI.OneDocument.Spreadsheets
```

Requires [.NET 10](https://dotnet.microsoft.com/download/dotnet/10.0) (net10.0); .NET 9 and earlier aren’t supported.

```csharp
using RecursiveAI.OneDocument.Spreadsheets;
using RecursiveAI.OneDocument.Spreadsheets
    .Streaming;

using var book = XlsxStreamReader.Open(
    "input.xlsx");
using var sheet = book.OpenSheet("Data");
double total = 0;
while (sheet.ReadRow())
    while (sheet.ReadCell())
        if (sheet.Kind == CellValueKind.Number)
            total += sheet.Number;
Console.WriteLine($"Total: {total}");
```

```text
Total: 263
```

`input.xlsx` has a `Data` sheet with these cells:

```text
Customer | Total | Id
Ada      | 42    | 101
Lin      | 18    | 102
```

Malformed XML raises `OfficeFormatException`; resource limits raise `OfficeLimitException`, with the part and byte offset.

Unzip [the example](https://recursiveai.org/onedocument/cookbook/stream-large-excel-file/stream-large-excel-file.zip), then `dotnet run`.

## Variations

### Sum only the second column

Exclude the numeric Id column: the two amounts sum to 60 rather than 263.

```csharp
using RecursiveAI.OneDocument.Spreadsheets;
using RecursiveAI.OneDocument.Spreadsheets
    .Streaming;

using var book = XlsxStreamReader.Open(
    "input.xlsx");
using var sheet = book.OpenSheet("Data");
double total = 0;
while (sheet.ReadRow())
    while (sheet.ReadCell())
        if (sheet.Column == 2 &&
            sheet.Kind == CellValueKind.Number)
            total += sheet.Number;
Console.WriteLine($"Total: {total}");
```

```text
Total: 60
```

## Related recipes

- [Create an Excel file](https://recursiveai.org/onedocument/cookbook/create-excel-file/)
- [Read an Excel file](https://recursiveai.org/onedocument/cookbook/read-excel-file/)
- [Read Excel into a list of objects](https://recursiveai.org/onedocument/cookbook/read-excel-rows-as-objects/)

## The short answer

Use `XlsxStreamReader` and a `SheetCursor` to visit rows and cells without building the workbook model. Open the file, open the sheet, and loop over `ReadRow()` and `ReadCell()`. The sample sums numeric cells and prints `Total: 263`, including the numeric Id column. It uses a tiny workbook so the download is small; the same traversal works when the sheet contains more rows.

Streaming is useful when the operation is a single pass: aggregating amounts, filtering records or sending values to another destination. It is not the right abstraction for arbitrary edits across a workbook. The cursor moves forward, and values are valid only while it is positioned on their cell.

## How do I work with very large Excel files?

Avoid loading objects you do not need. The example reads a cell kind before taking its numeric value and never constructs a list of rows. The variation additionally checks the column, so a numeric customer identifier in another column would not enter the amount total. For real imports, define which sheet and columns belong to the calculation rather than adding every number you encounter.

Keep both the reader and sheet cursor alive during traversal, and dispose them when finished. After a successful `ReadRow()`, row properties describe that row. After a successful `ReadCell()`, cell properties describe that cell until the cursor advances. Do not retain a text span and use it after another read. If a consumer needs an owned string, `GetText()` supplies one, with the allocation that entails.

Calling `ReadRow()` also skips cells you did not consume from the previous row. This permits a job that stops examining a row once it has the required columns. It does not permit jumping backward to a previously skipped cell. Rows and cells are delivered in stored order; out-of-order input is reported and is not sorted into a new table for you.

“Streaming” does not mean fixed memory for every input. The [cursor documentation](https://recursiveai.org/onedocument/docs/spreadsheets-streaming/) bounds memory by the largest token in the sheet plus the shared string table when text is read. Many distinct shared strings or one unusually long value can therefore affect memory even when you never retain a row yourself. Cell data is not held as a workbook model.

## Can C# read an Excel file?

Yes. This recipe uses the published spreadsheets package to read XLSX directly. Its streaming namespace also supports XLSM and Flat OPC. Separate binary readers exist for XLS and XLSB; renaming a binary file with an XLSX extension does not make it an XLSX archive.

The cursor returns cached formula results, not newly evaluated formulas. If the source file has stale caches, the aggregation can be stale too. Decide whether the producer is responsible for calculating before delivery. Merely opening a file through this reader is not a calculation request.

Choose the data representation based on the next step. Numeric, Boolean and error properties avoid constructing strings. UTF-8 text spans are useful for immediate consumption; string-returning methods allocate. If your task needs named columns mapped to business objects, the [typed row reader](https://recursiveai.org/onedocument/cookbook/read-excel-rows-as-objects/) sits on streaming cursors and handles that mapping for you. Avoid adding `ToList()` when the purpose is to process an unbounded sequence one row at a time.
