IT 手制品

悠云闲语

jxlsb - Java XLSB 库(商业版)

jxlsb - Java XLSB 库(商业版)

纯Java实现的XLSB(Excel Binary Workbook)格式读写库,包含企业级功能。

特性

  • 零依赖:仅依赖SLF4J,无需POI等重型库
  • 堆外内存:全量堆外内存架构,零GC压力
  • 高性能:比POI快3x,比EasyExcel快2.5x,文件小35-50%
  • 企业级:Java 8+支持,Multi-Release JAR(Java 23+自动使用Foreign Memory API)
  • 加密功能:AES-128+SHA1标准加密,WPS/Excel兼容
  • 水印功能:文本框水印,自定义文本、位置、字体、颜色、重复模式

性能数据

100K行 × 10列:

文件大小 写入时间 格式
jxlsb 2.72 MB 453 ms XLSB
FastExcel 5.42 MB 521 ms XLSX
EasyExcel 4.21 MB 1121 ms XLSX
POI 4.16 MB 1528 ms XLSX

1M行 × 10列:

文件大小 写入时间 格式
jxlsb 26.71 MB 4647 ms XLSB
FastExcel 55.00 MB 4621 ms XLSX
EasyExcel 42.54 MB 9405 ms XLSX
POI 42.25 MB 8334 ms XLSX

API 场景适配

写入 API

API 适用场景 数据来源 内存压力 示例
writeBatch 计算报表、内存数据导出 内存已有 / 实时计算 函数式一次性写入
startSheet + writeRows + endSheet 数据库分页查询、大文件流式处理 DB分页 / 文件流 分批追加写入
template + fillBatch/fillAtMarker 模板填充、报表生成 模板 + 数据 保留模板样式

模板填充 API

基于XLSB模板填充数据,保留模板所有内容(样式、合并单元格等):

// 创建模板填充Writer
XlsbWriter writer = XlsbWriter.builder()
.template(Paths.get("template.xlsb")) // 模板路径
.path(Paths.get("output.xlsb")) // 输出路径
.build();

// 方式1: 固定位置填充
List<List<Object>> data = Arrays.asList(
Arrays.asList("张三", "北京", 25, "男"),
Arrays.asList("李四", "上海", 30, "女")
);
writer.fillBatch(0, data, 4, 2); // sheetIndex, dataList, startRow, startCol

// 方式2: 标记查找填充
writer.fillAtMarker("${data}", data); // 查找${data}标记位置填充

// 方式3: 流式填充
writer.startFill(0, 12, 8);
writer.fillRows(batch1);
writer.fillRows(batch2);
writer.endFill();

writer.close();

模板支持范围

  • ✅ 保留模板所有内容:styles.bin、theme、静态文本等
  • ✅ 保留单元格样式:字体、边框、填充、对齐
  • ✅ 保留合并单元格
  • ✅ 支持标记查找填充(如${data}
  • ⚠️ 仅支持表头模板:数据从指定位置向下填充,不支持头尾都有模板
  • ❌ 不支持尾部模板:填充数据后无法保留底部静态内容

读取 API

API 适用场景 数据量 示例
forEachRow 流式处理、数据清洗 任意 回调处理每行
readRows 分页读取、批量处理 大文件 List/Array批量返回

场景选择指南

写入场景

// 场景1: 内存数据导出(推荐 writeBatch)
List<Product> products = cache.getAll(); // 已在内存
writer.writeBatch("Products", (row, col) -> toCell(products.get(row), col), products.size(), 5);

// 场景2: 数据库分页导出(推荐 writeRows 流式追加)
writer.startSheet("Orders", 5);
int offset = 0;
while (true) {
List<Order> batch = db.query(offset, 1000); // 分页查询,避免OOM
if (batch.isEmpty()) break;
writer.writeRows(batch, offset, (order, col) -> toCell(order, col));
offset += batch.size();
}
writer.endSheet();

读取场景

// 场景1: 流式处理(推荐 forEachRow)
reader.forEachRow(0, new RowConsumer() {
void onCell(int row, int col, CellData data) {
// 直接处理,无需存储
processCell(data);
}
});

// 场景2: 分页批量处理(推荐 readRows)
int offset = 0;
while (true) {
List<CellData[]> batch = reader.readRows(0, offset, 1000);
if (batch.isEmpty()) break;
batchProcess(batch); // 批量处理1000行
offset += 1000;
}

快速开始

Maven依赖

<dependency>
<groupId>cn.itcraft</groupId>
<artifactId>jxlsb-commercial</artifactId>
<version>1.1.0</version>
</dependency>

写入示例

import cn.itcraft.jxlsb.api.*;
import java.nio.file.Paths;

// 一次性写入(内存数据)
try (XlsbWriter writer = XlsbWriter.builder().path(Paths.get("output.xlsb")).build()) {
writer.writeBatch("Sheet1", (row, col) -> CellData.number(row * col), 1000, 10);
}

// 分页追加写入(数据库查询)
try (XlsbWriter writer = XlsbWriter.builder().path(Paths.get("output.xlsb")).build()) {
writer.startSheet("Orders", 4);
int offset = 0;
while (true) {
List<Order> batch = db.query(offset, 1000);
if (batch.isEmpty()) break;
writer.writeRows(batch, offset, (order, col) -> {
switch (col) {
case 0: return CellData.number(order.getId());
case 1: return CellData.text(order.getName());
case 2: return CellData.number(order.getAmount());
case 3: return CellData.date(order.getTime());
default: return CellData.blank();
}
});
offset += batch.size();
}
writer.endSheet();
}

读取示例

import cn.itcraft.jxlsb.api.*;

try (XlsbReader reader = XlsbReader.builder().path(Paths.get("data.xlsb")).build()) {
// 流式处理
reader.forEachRow(0, new RowConsumer() {
void onCell(int row, int col, CellData data) {
System.out.println(row + "," + col + ": " + data.getValue());
}
});

// 分页批量读取
int offset = 0;
while (true) {
List<CellData[]> batch = reader.readRows(0, offset, 1000);
if (batch.isEmpty()) break;
// 处理batch
offset += batch.size();
}
}

单元格类型

CellData.text("Hello")       // 文本
CellData.number(3.14159) // 数字
CellData.date(timestamp) // 日期(毫秒时间戳)
CellData.bool(true) // 布尔
CellData.blank() // 空白

// 数字格式(支持百分比、千分位、负红、货币等)
CellData.percentage(0.1234) // 0.00%
CellData.numberWithComma(1234567.89) // #,##0.00
CellData.numberNegativeRed(-1234.56) // #,##0.00;[Red]-#,##0.00
CellData.currency(1234.56) // ¥#,##0.00
CellData.time(timestamp) // h:mm:ss

功能状态

功能 状态 说明
数字单元格 ✅ 完整 支持整数、浮点数
文本单元格 ✅ 完整 SST优化,大文本支持
布尔单元格 ✅ 完整
日期单元格 ✅ 完整 Excel日期序列号
空白单元格 ✅ 完整
样式系统 ✅ 完整 字体、边框、填充、对齐
数字格式 ✅ 完整 自定义格式字符串
流式写入 ✅ 完整 startSheet/writeRows/endSheet
流式读取 ✅ 完整 forEachRow回调
分页读取 ✅ 完整 readRows批量返回
模板填充 ✅ 完整 fillBatch/fillAtMarker/startFill
合并单元格 ✅ 完整 模板中合并单元格保留
文件加密 ✅ 完整 AES-128+SHA1,WPS/Excel兼容
文件解密 ✅ 完整 密码验证+数据解密
文本框水印 ✅ 完整 自定义文本、位置、字体、颜色、重复模式
公式 ❌ 不支持
图表 ❌ 不支持
条件格式 ❌ 不支持
宏/VBA ❌ 不支持

功能组合使用

模板填充 + 水印 + 加密组合示例:

import cn.itcraft.jxlsb.api.*;
import cn.itcraft.jxlsb.encryption.*;
import cn.itcraft.jxlsb.cfb.*;
import java.nio.file.*;
import java.io.*;
import java.util.*;

// 1. 模板填充 + 水印
Path tempPath = Paths.get("/tmp/temp.xlsb");
List<List<Object>> data = Arrays.asList(
Arrays.asList("张三", "北京", 25, "男"),
Arrays.asList("李四", "上海", 30, "女")
);

try (XlsbWriter writer = XlsbWriter.builder()
.template(Paths.get("template.xlsb"))
.path(tempPath)
.build()) {
writer.fillBatch(0, data, 12, 8); // 填充数据
writer.setWatermark("内部资料"); // 添加水印
}

// 2. 加密
byte[] zipData = Files.readAllBytes(tempPath);
StandardEncryptor encryptor = new StandardEncryptor();
encryptor.confirmPassword("password123");

byte[] encryptedPackage = encryptor.encryptPackage(zipData);
StandardEncryptionInfo encInfo = new StandardEncryptionInfo(
encryptor.getSalt(), encryptor.getVerifier(), encryptor.getVerifierHash());

CfbWriter cfb = new CfbWriter();
cfb.addStream("EncryptionInfo", encInfo.serialize());
cfb.addStream("EncryptedPackage", encryptedPackage);
cfb.writeTo(new FileOutputStream("encrypted.xlsb"));

Files.delete(tempPath); // 清理临时文件

水印功能

功能概述

支持在XLSB文件中添加文本框水印,WPS/Excel打开后可见水印文字。

API示例

import cn.itcraft.jxlsb.watermark.TextBoxWatermark;

// 基础用法 - 单水印
writer.setWatermark("内部资料");

// 自定义水印
TextBoxWatermark watermark = TextBoxWatermark.builder()
.text("机密文件")
.fontSize(28) // 字体大小(pt)
.color(180, 180, 180) // 颜色(RGB或十六进制)
.coverData(0, 0, cols-1, rows-1) // 覆盖数据区域
.build();
writer.setWatermark(watermark);

// 重复水印
TextBoxWatermark watermark = TextBoxWatermark.builder()
.text("水印")
.fontSize(20)
.repeatEvery(25) // 每25行添加一个水印
.coverData(0, 0, cols-1, 24) // 每个水印覆盖25行
.build();
writer.setWatermark(watermark);

参数说明

参数 API 说明
文本 text(String) 水印文字
字体大小 fontSize(int pt) 字号,单位磅
颜色 color(int r, int g, int b) RGB颜色
颜色 color(String hex) 十六进制颜色(如”FF0000”)
覆盖数据 coverData(col1, row1, col2, row2) 水印覆盖指定数据区域
位置 position(col1, row1, col2, row2) 直接指定水印位置
单水印 single() 单个水印(默认)
重复水印 repeatEvery(int rows) 每N行重复一个水印

加密功能

加密/解密性能对比

规模 普通写 加密写 普通读 解密读 文件增量
1K×10 48ms / 32KB 53ms (+10%) 18ms 17ms +3KB
10K×10 73ms / 292KB 50ms 14ms 13ms +2.7KB
50K×10 255ms / 1.4MB 249ms 38ms 30ms +2.9KB
100K×10 401ms / 2.7MB 446ms (+11%) 37ms 43ms (+16%) +3KB

结论

  • 加密开销极小(0-11%),主要来自PBKDF2密钥派生(50000次SHA1迭代)
  • 解密开销0-16%
  • 文件增量固定约3KB(CFB结构),与数据规模无关

加密API

import cn.itcraft.jxlsb.encryption.*;

// 加密文件
StandardEncryptor encryptor = new StandardEncryptor();
encryptor.confirmPassword("your_password");

byte[] encryptedPackage = encryptor.encryptPackage(zipData);

StandardEncryptionInfo encInfo = new StandardEncryptionInfo(
encryptor.getSalt(),
encryptor.getVerifier(),
encryptor.getVerifierHash()
);

CfbWriter cfb = new CfbWriter();
cfb.addStream("EncryptionInfo", encInfo.serialize());
cfb.addStream("EncryptedPackage", encryptedPackage);
cfb.writeTo(new FileOutputStream("encrypted.xlsb"));

解密API

import cn.itcraft.jxlsb.encryption.*;

// 解密文件
EncryptedFileReader reader = new EncryptedFileReader("encrypted.xlsb");

StandardDecryptionInfo decInfo = reader.readEncryptionInfo();
StandardDecryptor decryptor = decInfo.createDecryptor();

if (decryptor.verifyPassword("your_password")) {
byte[] decryptedZipData = reader.decryptWithPassword("your_password");
// 使用解密后的ZIP数据
}

生产就绪评估

推荐场景

  • ✅ 大数据量Excel导出(100K-1M行)
  • ✅ 数据库分页查询导出
  • ✅ 存储成本敏感(文件小50%)
  • ✅ 内存受限环境(堆外内存)
  • ✅ 模板报表生成(保留样式、合并单元格)
  • ✅ 企业安全需求(加密、水印)

不推荐场景

  • ❌ 需要公式、图表
  • ❌ 需要条件格式
  • ❌ 需要头尾都有模板的报表(仅支持表头模板)

架构

┌─────────────────────────────────────────┐
│ API Layer │
│ XlsbWriter / XlsbReader │
│ writeBatch / writeRows / readRows │
├─────────────────────────────────────────┤
│ Data Structure Layer │
│ CellData / RowDataSupplier │
├─────────────────────────────────────────┤
│ Memory Management Layer │
│ ByteBufferAllocator (Java 8) │
│ MemorySegmentAllocator (Java 23+) │
├─────────────────────────────────────────┤
│ BIFF12 Format Layer │
│ BrtCellRk / BrtCellReal / BrtCellIsst │
├─────────────────────────────────────────┤
│ IO Layer │
│ ZipContainer / FileChannel │
└─────────────────────────────────────────┘

测试覆盖

  • 150个测试全部通过
  • 内存层:分配、读写、关闭、泄漏检测
  • 格式层:BIFF12记录、VarInt编码
  • API层:写入、读取、流式追加
  • 加密:循环测试、密码验证
  • 水印:单水印、重复水印、组合测试
  • 性能测试:100K/1M行对比

License

商业许可证 - 联系 hellguo#foxmail.com 获取授权

0%