How to Read an Excel File in Java (XLSX Parsing Guide)

Reading Excel files in Java is a common starting point for processing customer uploads, importing business records, or checking data from another system. In this guide, we’ll show how to read an Excel file in Java with Axyra Sheets for Java: open an XLSX workbook, locate its populated cells, and extract values in bulk. We’ll also handle dates, formatted text, formula results, and error cells so your application can interpret the data correctly.

The same code you use to read XLSX in Java also reads XLSM, XLSB, XLS, ODS, CSV, and JSON.

Writing a file instead? See How to Create an Excel File in Java.

What you need

Java 17 or later plus the dependency; the native libraries ship inside the jar. See Installation if you need the Gradle coordinates or hit a platform problem. For a short walkthrough of Axyra before diving in, see Quick Start.

<dependency>
    <groupId>io.keikai</groupId>
    <artifactId>axyra-sheets</artifactId>
    <version>1.0.0-Beta.FL.20261013-Eval</version>
</dependency>

The artifact is served from ZK’s repository rather than Maven Central, so your build also needs the <repositories> entry shown in Installation.

Step 1 — Open the workbook

The format is detected from the file name, so one call covers every supported format.

import io.keikai.axyra.sheets.Workbook;
import java.nio.file.Path;

try (Workbook wb = Workbook.open(Path.of("report.xlsx"))) {
    // ...
}

When the file arrives over HTTP or comes out of a database there is no name to infer from, so state the format explicitly:

byte[] bytes = ...;
try (Workbook wb = Workbook.openBytes(bytes, "xlsx")) { ... }

try (var in = request.getInputStream();
     Workbook wb = Workbook.open(in, "xlsx")) { ... }

Encrypted workbooks take a password on the same call:

Workbook wb = Workbook.open(Path.of("secret.xlsx"), "pa55w0rd");
Workbook fromMemory = Workbook.openBytes(bytes, "xlsx", "pa55w0rd");

Always close the workbook. It owns native memory outside the JVM heap that the garbage collector cannot reclaim, and Sheet, Range, and Cell are views that become invalid the moment it closes. Do not return one from the method that opened it.

Step 2 — Walk the sheets

import io.keikai.axyra.sheets.Sheet;

for (int i = 0; i < wb.sheetCount(); i++) {
    Sheet sheet = wb.sheet(i);
    System.out.println(i + ": " + sheet.name() + " (" + sheet.sheetType() + ")");
}

Sheet byName = wb.sheet("Q3 Sales");

Prefer looking a sheet up by name over a fixed index. A file saved by Axyra Sheets in Evaluation Mode starts with an extra Axyra Evaluation Copy sheet, which shifts every index by one.

sheetType() matters when you are iterating blindly: a workbook can contain chart sheets, dialog sheets, and macro sheets, and only WORKSHEET holds a grid. Hidden sheets are included in the count — check sheet.visibility() if you want to skip them.

Step 3 — Find where the data is

Do not guess the extent. usedRange() returns the populated block, or null for an empty sheet.

import io.keikai.axyra.sheets.Range;

Range used = sheet.usedRange();
if (used == null) {
    return;   // nothing on this sheet
}
System.out.printf("data in rows %d-%d, cols %d-%d (%d x %d)%n",
        used.firstRow(), used.lastRow(),
        used.firstColumn(), used.lastColumn(),
        used.rowCount(), used.colCount());

Remember that these indices are 0-based, so firstRow() == 0 is spreadsheet row 1.

Step 4 — Read the Excel values in bulk

One Range call crosses the JNI boundary once for the whole block. A loop of sheet.value(r, c) crosses it once per cell, which is the difference between milliseconds and minutes on a large sheet.

import io.keikai.axyra.sheets.CellValue;

CellValue[][] grid = used.values();      // row-major
for (CellValue[] row : grid) {
    for (CellValue v : row) {
        // ...
    }
}

When you know the block is numeric, numbers() is faster still — a flat, row-major double[] with no boxing and no CellValue allocation:

double[] flat = sheet.range("B2:D10000").numbers();

To read the formulas rather than their results:

String[][] formulas = used.formulas();   // null entries where there is no formula

Step 5 — Handle each value type

CellValue is a sealed interface with exactly six variants, so instanceof patterns cover the space:

static String describe(CellValue v) {
    if (v instanceof CellValue.Number n) return "number " + n.value();
    if (v instanceof CellValue.Text t)   return "text " + t.value();
    if (v instanceof CellValue.Bool b)   return "bool " + b.value();
    if (v instanceof CellValue.Error e)  return "error " + e.display();  // "#DIV/0!"
    if (v instanceof CellValue.Array a)  return a.rowCount() + "x" + a.colCount() + " array";
    return "(blank)";
}

On Java 21 or later you can switch over the variants instead (case CellValue.Number n -> ...) and let the compiler prove exhaustiveness. The Java library targets Java 17, where pattern matching for switch is not yet available, so the samples here use instanceof.

If you want what a spreadsheet application would show — the number format applied, so 1499 becomes $1,499.00 — read the formatted text instead of the raw value:

String shown = sheet.cell(4, 2).formattedText();

That is a per-cell call, so use it for display, not for bulk extraction.

Step 6 — Formula results and error cells

A formula cell carries the value the writing application cached, so reading it usually needs no work on your part. Your own edits recalculate the formulas that depend on them, but formulas you have not touched keep the cached result. Recalculate once when you do not trust that cache — a file written by another tool, or one with volatile functions such as NOW():

wb.recalculate();        // the whole workbook; call it once, not per cell

A formula error is a value, not an exception. #DIV/0! arrives as CellValue.Error, and AxyraFormulaException is reserved for a malformed request — a formula string that will not parse. So a sheet full of #REF! reads cleanly; you decide what to do about it.

if (v instanceof CellValue.Error e) {
    log.warn("cell holds {} (ERROR.TYPE {})", e.display(), e.code());
}

You can also evaluate an expression against the open workbook without writing it anywhere:

CellValue total = wb.evaluate("SUM('Q3 Sales'!B2:B100)");

Step 7 — Dates

Spreadsheets store dates as serial numbers, so a date cell reads back as CellValue.Number. Convert through the cell:

import java.time.LocalDate;
import java.time.LocalDateTime;

LocalDate d = sheet.cell(1, 0).dateValue();
LocalDateTime dt = sheet.cell(1, 1).dateTimeValue();

Which epoch the serial refers to depends on the workbook’s date1904 flag, which the conversion already accounts for — see Cells and Ranges.

The complete program to read an Excel file in Java

Reads every worksheet and prints the used range as TSV.

import io.keikai.axyra.sheets.CellValue;
import io.keikai.axyra.sheets.Range;
import io.keikai.axyra.sheets.Sheet;
import io.keikai.axyra.sheets.SheetType;
import io.keikai.axyra.sheets.Workbook;

import java.nio.file.Path;

public class ReadExcelFile {

    public static void main(String[] args) {
        try (Workbook wb = Workbook.open(Path.of("report.xlsx"))) {
            for (int i = 0; i < wb.sheetCount(); i++) {
                Sheet sheet = wb.sheet(i);
                if (sheet.sheetType() != SheetType.WORKSHEET) {
                    continue;
                }

                Range used = sheet.usedRange();
                if (used == null) {
                    System.out.println("== " + sheet.name() + " (empty)");
                    continue;
                }

                System.out.printf("== %s  rows %d-%d, cols %d-%d%n",
                        sheet.name(),
                        used.firstRow(), used.lastRow(),
                        used.firstColumn(), used.lastColumn());

                for (CellValue[] row : used.values()) {
                    StringBuilder line = new StringBuilder();
                    for (CellValue v : row) {
                        if (line.length() > 0) {
                            line.append('\t');
                        }
                        line.append(text(v));
                    }
                    System.out.println(line);
                }
            }
        }
    }

    private static String text(CellValue v) {
        if (v instanceof CellValue.Number n) return String.valueOf(n.value());
        if (v instanceof CellValue.Text t)   return t.value();
        if (v instanceof CellValue.Bool b)   return String.valueOf(b.value());
        if (v instanceof CellValue.Error e)  return e.display();
        if (v instanceof CellValue.Array a)  return "[array]";
        return "";
    }
}

Common mistakes when reading Excel files in Java

Symptom Cause
Reading is unbearably slow A per-cell loop. Use Range.values() or numbers() — one crossing for the whole block.
Everything is off by one row Indices are 0-based; spreadsheet row 1 is index 0.
IllegalStateException from a Sheet or Range The workbook was closed. Views do not outlive it — extract what you need before closing.
A date reads as 50000.0 That is the serial number. Use cell.dateValue() / dateTimeValue().
A currency cell reads as 1499.0, not $1,499.00 values() gives raw values. formattedText() applies the number format.
Formula cells look stale You are seeing the cache the writing tool left. Call recalculate().
Iterating sheets hits something with no grid Chart, dialog, and macro sheets are sheets too. Filter on sheetType().

Frequently asked questions

How do I read an Excel file in Java?

Open the workbook with Workbook.open(Path.of("report.xlsx")) in a try-with-resources block, get each worksheet’s populated block with sheet.usedRange(), and read all of its values in one call with Range.values(). Each value is a CellValue that you check by type: number, text, boolean, error, array, or blank. The complete program above shows the whole flow.

How do I read an XLSX file in Java from an upload or a database?

When there is no file name to detect the format from, pass the format explicitly. Use Workbook.openBytes(bytes, "xlsx") for a byte array or Workbook.open(in, "xlsx") for an InputStream, such as the body of an HTTP request.

Why is my Java code slow when reading an Excel file?

The usual cause is reading one cell at a time with sheet.value(r, c), which crosses the JNI boundary once per cell. Reading the whole block with Range.values(), or with numbers() for numeric data, crosses it once.

How do I read dates from an Excel file in Java?

Excel stores dates as serial numbers, so a date cell reads back as CellValue.Number. Call cell.dateValue() or cell.dateTimeValue() to get a LocalDate or LocalDateTime. The conversion accounts for the workbook’s date1904 flag.

When I read a formula cell, do I get the formula or the result?

values() returns the result that the writing application cached, and formulas() returns the formula strings. If you do not trust the cached results, call wb.recalculate() before reading.

Next steps

Beyond Reading Excel Data

Reading an Excel file in Java is usually the first step of a larger task. Once your application can read spreadsheet values, it can check them against business rules, map them to database records, or pass them to another service. With Axyra Sheets, you can also update the open workbook, recalculate formulas, and save the result or render it as PDF. This lets an Excel import grow into a workflow that reads incoming data, processes it, and returns an updated report.

Get code at GitHub