Apache Fesod: High-Performance Java Excel Library with Streaming & Spring Boot Integration

Apache Fesod (Incubating) is a high-performance, memory-efficient Java library for reading and writing Excel files, featuring streaming APIs, simple annotation-driven mapping, Spring Boot integration, and SXSSF-based large-file writing with optional temp-file compression.

Spring Full-Stack Practical Cases
Spring Full-Stack Practical Cases
Spring Full-Stack Practical Cases
Apache Fesod: High-Performance Java Excel Library with Streaming & Spring Boot Integration

Introduction

Apache Fesod (Incubating) is a high-performance, memory-efficient Java library for reading and writing spreadsheet files. The name stands for "fast easy spreadsheet and other documents" (pronounced /ˈfɛsɒd/). It aims to simplify development while ensuring reliability, with plans for future feature enhancements.

Key Features

High-performance read/write: Optimized for large-scale spreadsheet data, significantly reducing memory consumption compared to traditional libraries.

Simple API: Intuitive, annotation-driven mapping for easy integration into projects, from simple operations to complex data processing.

Streaming operations: Supports streaming reads to minimize memory footprint when processing hundreds of thousands or millions of rows.

Environment

Spring Boot 3.5.0, Java 1.8+ (latest LTS recommended).

Dependency Configuration

Currently, Apache Fesod uses POI as the underlying package. If your project already includes POI components, you must manually exclude POI-related JARs.
<dependency>
  <groupId>org.apache.fesod</groupId>
  <artifactId>fesod-sheet</artifactId>
  <version>2.0.2-incubating</version>
</dependency>

Quick Start

Reading Excel

Define a listener implementing ReadListener<User>:

public class UserDataListener implements ReadListener<User> {
  @Override
  public void invoke(User user, AnalysisContext context) {
    System.out.println("解析数据: %s".formatted(user));
  }
  @Override
  public void doAfterAllAnalysed(AnalysisContext context) {
    System.out.println("所有数据解析完成!");
  }
}

Read the file:

String fileName = "f:/user.xlsx";
FesodSheet.read(fileName, User.class, new UserDataListener())
  .sheet()
  .doRead();

Output example:

解析数据: User [id=1, name=张三, age=22, [email protected]]
解析数据: User [id=2, name=李四, age=33, [email protected]]
解析数据: User [id=3, name=王五, age=44, [email protected]]
所有数据解析完成!

Writing Excel

Model class with annotations:

public class User {
  @ExcelProperty("编号")
  private Long id;
  @ExcelProperty("姓名")
  private String name;
  @ExcelProperty("年龄")
  private Integer age;
  @ExcelProperty("邮箱")
  private String email;
  @ExcelIgnore
  private String profile;
}

Write data:

private static List<User> data() {
  List<User> list = new ArrayList<>();
  for (int i = 4; i <= 10; i++) {
    User user = new User(
      i + 0L,
      "姓名 - " + i,
      new Random().nextInt(100),
      i + "@qq.com",
      "profile - " + i
    );
    list.add(user);
  }
  return list;
}

public static void main(String[] args) {
  String fileName = "f:/user.xlsx";
  FesodSheet.write(fileName, User.class)
    .sheet("Template")
    .doWrite(data());
}

Spring Boot Integration

Upload Excel

public class UploadUserListener extends AnalysisEventListener<User> {
  private static final Logger log = LoggerFactory.getLogger(UploadUserListener.class);
  private final List<User> list = new ArrayList<>();
  @Override
  public void invoke(User user, AnalysisContext context) {
    list.add(user);
    // For large data, perform batch saves here
  }
  @Override
  public void doAfterAllAnalysed(AnalysisContext context) {
    log.info("所有数据读取完成!");
    // Save to database here
  }
}

@PostMapping("/upload")
public ResponseEntity<String> upload(@RequestParam("file") MultipartFile file) {
  if (file.isEmpty()) {
    return ResponseEntity.badRequest().body("请选择一个文件上传!");
  }
  try {
    FesodSheet.read(file.getInputStream(), User.class, new UploadUserListener())
      .sheet()
      .doRead();
    return ResponseEntity.ok("文件上传并处理成功!");
  } catch (IOException e) {
    e.printStackTrace();
    return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).body("文件处理失败!");
  }
}

Download Excel

@GetMapping("/download")
public void download(HttpServletResponse response) throws IOException {
  response.setContentType("application/vnd.openxmlformats-officedocument.spreadsheetml.sheet");
  response.setCharacterEncoding("utf-8");
  String fileName = URLEncoder.encode("user", "UTF-8").replaceAll("\\+", "%20");
  response.setHeader("Content-disposition", "attachment;filename*=utf-8''" + fileName + ".xlsx");
  FesodSheet.write(response.getOutputStream(), User.class)
    .sheet("Sheet")
    .doWrite(data());
}

Large File Writing

Exporting large datasets (database dumps, log analysis) can exhaust memory if all rows are loaded at once. Fesod internally uses Apache POI's streaming API (SXSSF), but temporary XML files may consume significant disk space. Enabling compression reduces disk usage at the cost of slightly higher CPU consumption.

@Test
public void largeFileWrite() {
  String fileName = "largeFile" + System.currentTimeMillis() + ".xlsx";
  try (ExcelWriter excelWriter = FesodSheet.write(fileName, User.class)
    .registerWriteHandler(new WorkbookWriteHandler() {
      @Override
      public void afterWorkbookCreate(WorkbookWriteHandlerContext context) {
        Workbook workbook = context.getWriteWorkbookHolder().getWorkbook();
        if (workbook instanceof SXSSFWorkbook) {
          ((SXSSFWorkbook) workbook).setCompressTempFiles(true);
        }
      }
    }).build()) {
    WriteSheet writeSheet = FesodSheet.writerSheet("模板").build();
    // Batch write — each data() call returns one batch
    for (int i = 0; i < 1000; i++) {
      excelWriter.write(data(), writeSheet);
    }
  }
}

Architecture

Data (in-memory, batched)      Fesod          POI/SXSSF
  │                            │              │
  ├─ 100-row batch ──────────▶ write() ─────▶ Temp XML (compressed)
  ├─ 100-row batch ──────────▶ write() ─────▶ Temp XML (appended)
  │  ... (1000 batches)        │              │
  └─ close() ────────────────▶ Finish ──────▶ Final .xlsx

Performance Recommendations

Use ExcelWriter (try-with-resources) for batched writes instead of loading all data via doWrite().

Enable temp-file compression in disk-constrained environments.

Tune batch size (e.g., 100 rows) based on row width and available memory.

Monitor temp directory size via FileUtils.getPoiFilesPath().

Original Source

Signed-in readers can open the original source through BestHub's protected redirect.

Sign in to view source
Republication Notice

This article has been distilled and summarized from source material, then republished for learning and reference. If you believe it infringes your rights, please contactadmin@besthub.devand we will review it promptly.

performance optimizationSpring BootStreaming APILarge File ProcessingAnnotation MappingApache FesodJava ExcelSXSSF
Spring Full-Stack Practical Cases
Written by

Spring Full-Stack Practical Cases

Full-stack Java development with Vue 2/3 front-end suite; hands-on examples and source code analysis for Spring, Spring Boot 2/3, and Spring Cloud.

0 followers
Reader feedback

How this landed with the community

Sign in to like

Rate this article

Was this worth your time?

Sign in to rate
Discussion

0 Comments

Thoughtful readers leave field notes, pushback, and hard-won operational detail here.