Apache Fesod: a memory-conscious Java library for reading and writing spreadsheets
Fast. Easy. Done. Processing spreadsheets without worrying about large files causing OOM.
At a glance
- What is it?
- Apache Fesod (Incubating) is a Java spreadsheet library built around stream reading and a listener API, aimed at teams whose XLSX files are large enough to threaten the heap. It is an Apache incubator project with a small API surface and POI underneath.
- Who is it for?
- Apache Fesod fits Java services that ingest or export spreadsheet files large enough that a full in-memory load is a risk, and it fits them best when the team is willing to work through a listener rather than a grid object. It is the wrong choice if you need the full POI object model, if you already depend on POI and do not want to manage exclusions, or if a non-JVM process can do the job.
- Can I use it commercially?
- Yes. Apache-2.0 is a permissive licence: you can use, modify and sell software built on it, as long as you keep its copyright and licence notices.
- Is it still maintained?
- Yes. The repository last received commits 1 day ago.
- What is it written in?
- Mainly Java, according to GitHub's language statistics.
Answers come from the project's GitHub data, last synced on September 29, 2026, and from our analysis. They are not legal advice.
Editorial analysis
What Fesod solves, and who ends up using it
The problem is the shape of a typical spreadsheet import. A file arrives with hundreds of thousands of rows, the obvious code path builds an object per row and holds them all, and the heap fills before the parsing finishes. The README describes Apache Fesod (Incubating) as a high-performance and memory-efficient Java library for reading and writing spreadsheet files, and its stated design goal is stream reading that avoids loading large amounts of data at once. That is the whole pitch: keep the working set small while rows flow past.
The audience is Java developers and the enterprises that employ them. Concretely, that means a service that accepts an uploaded XLSX, a batch job that exports a report, or a scheduled task that reconciles two spreadsheets. The project name is an acronym for fast easy spreadsheet and other documents, which tells you the intended scope is broader than one format. The topics list csv, xls and xlsx, so CSV sits alongside the Excel formats rather than being an afterthought.
It is not a general data-processing framework. If your rows are small and your files fit comfortably in memory, the streaming design buys you nothing and costs you the convenience of a materialized list.
How the streaming read actually works
The mechanism visible in the README is a callback interface. You implement ReadListener<T> and the library calls invoke for each parsed row, then doAfterAllAnalysed once the file is exhausted. The generic parameter is your own row class, so the library is responsible for mapping cells onto fields while you are responsible for what happens to each row. Because the row is handed to you and then discarded, the library never needs to retain the full sheet.
The entry point is FesodSheet.read, which takes a file name, the row class and a listener instance. The chain continues with .sheet() to select a sheet and .doRead() to start the work. That is a builder-style flow rather than a single call with many arguments, which leaves room for sheet selection and other options without changing the signature of read.
Underneath, the README states that Apache Fesod (Incubating) currently uses POI as its underlying package. So the streaming behaviour is layered on top of an existing parser rather than replacing it. The repository layout reflects a multi-module build: fesod-sheet holds the sheet-facing API, with fesod-common, fesod-bom and fesod-shaded alongside it. The shaded module is worth noting for anyone whose dependency tree already contains POI.
The write side follows the same philosophy. The README shows a read example in full and begins a write example using FesodSheet, so writing is part of the documented surface, but the excerpt does not show the write call itself. Treat the write API as something to confirm against the project documentation rather than infer from the read path.
Installing Fesod from Maven and reading a first file
The README gives Java 1.8 as the minimum and encourages the latest LTS release. It also recommends using the latest version of the library, on the grounds that performance work and fixes land there. The README's own snippets write the version as the literal placeholder version, so substitute the release you intend to pin; the most recent release listed for the project is 2.0.2-incubating.
For Maven, the artifact is fesod-sheet under the org.apache.fesod group. The README gives this block:
<dependency>
<groupId>org.apache.fesod</groupId>
<artifactId>fesod-sheet</artifactId>
<version>version</version>
</dependency>Gradle users get the equivalent coordinate, again with the placeholder version:
dependencies {
implementation 'org.apache.fesod:fesod-sheet:version'
}One warning sits directly above the installation section. Because Fesod uses POI underneath, the README says that if your project already includes POI-related components you will need to manually exclude the POI-related jar files. Do that before you debug anything else, or you will be chasing classpath conflicts that have nothing to do with your code.
Reading is two pieces. First a listener that receives each row and a completion callback:
public class DemoDataListener implements ReadListener<DemoData> {
@Override
public void invoke(DemoData data, AnalysisContext context) {
System.out.println("Parsed a data entry" + JSON.toJSONString(data));
}
@Override
public void doAfterAllAnalysed(AnalysisContext context) {
System.out.println("All data parsed!");
}
}Then a main method that names the file and starts the read. The README's example points at demo.xlsx and chains sheet selection and doRead:
public static void main(String[] args) {
String fileName = "demo.xlsx";
FesodSheet.read(fileName, DemoData.class, new DemoDataListener()).sheet().doRead();
}What you should see, per the example's own print statements, is one line per parsed row followed by the completion message. The JSON serialization in the sample is illustrative; the library does not require it. The first thing worth changing in a real project is the body of invoke, because printing every row of a large file is itself a way to make the process slow.
Where the listener design gets in your way
The callback model is the source of most of the friction. Anything you want to do with the whole dataset has to be assembled by you, row by row, inside invoke. Aggregations, cross-row validation and two-pass logic do not map cleanly onto a single forward pass, and the library does not offer a materialized collection as the default path. That is a deliberate trade, not an oversight, but it is a trade you pay for in application code.
Error handling is the second rough edge. If invoke throws, the README does not document what happens to the remaining rows or whether doAfterAllAnalysed still runs. A reader who needs partial-failure semantics should test that case directly rather than assume either behaviour. Similarly, the README does not document rollback, so there is no basis for expecting transactional reading of a sheet.
The dependency situation is a real constraint rather than a footnote. Bringing Fesod into a project that already uses POI means resolving overlapping jars yourself, and the README's instruction to exclude them is a manual step with manual consequences. Teams with a large existing POI investment should weigh whether the streaming read is worth that surgery.
Finally, the project is under the Apache incubator, and the repository's release history is short: 2.0.0-incubating in January 2026, 2.0.1-incubating in February, 2.0.2-incubating in May. That cadence suggests movement, but incubating status also means the governance and release process are still maturing. The last push to the repository was on 2026-09-20.
Fesod against raw Apache POI
The honest alternative in the Java world is Apache POI itself, which Fesod already depends on. The difference is not the file format support; it is the API shape and the default memory posture. POI gives you workbook and sheet objects you can navigate freely, which is convenient for random access and awkward for very large files, where the object model is exactly the thing you were trying to avoid. Fesod's ReadListener inverts that: you give up random access and get a forward-only pass with a small working set.
If your files are modest, POI's model is simpler to reason about and you already know it. If your files are the reason you are reading this page, the listener approach is the point.
For teams that are not committed to the JVM, the comparison is different rather than closer. A Python or Go process that streams a spreadsheet sidesteps the heap question entirely, at the cost of running a second runtime next to your Java service. That is a deployment decision, not a library decision, and it is worth making explicitly rather than by default.
Licence, releases and what upgrading costs
Apache Fesod is licensed under Apache-2.0, and the repository carries the standard Apache files: LICENSE, NOTICE, DISCLAIMER and a licenserc.toml for header checks. A permissive licence removes the redistribution question that a copyleft dependency would raise, but the NOTICE and DISCLAIMER files still travel with the project, and the incubating designation is a status signal rather than a legal one. This is a description of what the repository contains, not legal advice; your own counsel decides what your product must ship.
The upgrade cost is dominated by the POI relationship. Because Fesod wraps POI, a POI upgrade in your tree and a Fesod upgrade are coupled events, and the README's exclusion instruction means you are already managing that boundary by hand. Pinning a specific fesod-sheet version, rather than tracking the newest, keeps that boundary stable.
The release history is short enough to read in full: 2.0.0-incubating, 2.0.1-incubating and 2.0.2-incubating, all within 2026. There is no long tail of patch releases to mine for compatibility notes. The project's own advice is to use the latest version, which is sound for a young library but sits in tension with the caution that pinning usually provides.
Fesod's documentation and where to go next
The README points at fesod.apache.org as the project home and the place to find documentation, and it lists a mailing list at [email protected] for subscribing. There is also a DeepWiki link in the badge row, which is a generated documentation view rather than a primary source.
The gap that matters is the write path. The README's read example is complete enough to run, while the write example is cut off before the call that performs the write. Anyone whose use case is export rather than import should start at the project site rather than extrapolate from the read API, because the two are not guaranteed to mirror each other.
The repository also carries a website directory and a tools directory, so the documentation source lives in the same tree as the code. For a project at this stage, reading the module layout (fesod-sheet, fesod-common, fesod-bom, fesod-shaded) is a faster way to understand the intended boundaries than reading the introduction.
Editorial conclusion
Apache Fesod fits Java services that ingest or export spreadsheet files large enough that a full in-memory load is a risk, and it fits them best when the team is willing to work through a listener rather than a grid object. It is the wrong choice if you need the full POI object model, if you already depend on POI and do not want to manage exclusions, or if a non-JVM process can do the job. Before adopting it, build one reader against a representative file, watch the heap under a load you control, and confirm which fesod-sheet version you are pinning and which POI jars it pulls in.
Frequently asked questions
Which Maven coordinates do I use for Apache Fesod?
The README gives the group as org.apache.fesod and the artifact as fesod-sheet, with the version written as a placeholder in its example. The README also points at Maven Central for the fesod-sheet artifact.
What Java version does Apache Fesod require?
The README states that Apache Fesod (Incubating) requires Java 1.8 or later and encourages using the latest LTS release of Java.
Do I need to exclude POI when adding Apache Fesod to a project that already uses POI?
Yes. The README states that Fesod currently uses POI as its underlying package and that if your project already includes POI-related components you will need to manually exclude the POI-related jar files.
Official sources
Add this badge to your README
If you maintain this project, the badge below links readers to this analysis and shows its maintenance status from the daily GitHub snapshot. Paste the markdown into your README; add ?metric=license or ?metric=stars to the image URL for a different field.
[](https://hysenlabs.com/projects/apache-fesod)