yangweijie / stream-xlsx
Memory-efficient XLSX export builder powered by xlswriter C extension.
v1.0.0
2026-08-08 06:00 UTC
Requires
- php: ^7.4 || ^8.0
- ext-xlswriter: *
Suggests
- illuminate/support: Required for Laravel integration.
README
# StreamXlsx > Memory-efficient XLSX/CSV export builder powered by [xlswriter](https://github.com/viest/php-ext-xlswriter) C extension. [](https://php.net) [](LICENSE) --- ## Why PhpSpreadsheet holds every cell in memory. Export 100,000 rows × 10 columns and you're looking at ~500MB peak RAM. StreamXlsx uses the [xlswriter](https://github.com/viest/php-ext-xlswriter) C extension to write rows directly to disk — **constant ~3MB memory, regardless of row count.** | | PhpSpreadsheet (SpreedCore) | StreamXlsx (xlswriter) | |---|---|---| | 10 万行 × 10 列 内存 | ~480MB | ~3MB | | 10 万行 耗时 | ~25s | ~3s | | 依赖 | PHP 8.0+, Composer 包 | PHP 7.4+, C 扩展 | | PhpSpreadsheet 依赖 | 是 | **否** | --- ## Requirements - PHP ≥ 7.4 - [xlswriter](https://github.com/viest/php-ext-xlswriter) 扩展 (`ext-xlswriter`) - (可选)Laravel /illuminate/support — 用于 Laravel 响应集成 ### 安装 xlswriter 扩展 ```bash pecl install xlswriter echo "extension=xlswriter.so" >> php.ini
Installation
composer require yourvendor/stream-xlsx
Quick Start
use StreamXlsx\Engine\XlswriterBuilder; XlswriterBuilder::make() ->headers(['ID', 'Name', 'Email', 'Amount', 'Created At']) ->rows(User::cursor()) // Generator — memory stays flat ->headerColor('#0F4C81') ->headerBold() ->alignCenter() ->currency('D') // D 列货币格式 ->datetime('E') // E 列日期时间格式 ->freezeHeader() // 冻结表头 ->filter() // 自动筛选 ->alternateColor('#F5F5F5') // 隔行变色 ->autoWidth() // 自动列宽 ->download('users.xlsx');
Output Modes
// 浏览器下载 $builder->download('report.xlsx'); // 存储到磁盘路径 $builder->store('/var/exports/report.xlsx'); // 浏览器内联预览 $builder->stream('report.xlsx'); // 获取原始文件内容(字符串) $raw = $builder->raw('report.xlsx'); // CSV 模式(更轻量,无样式) $builder->download('report.csv');
API Reference
Headers
平铺表头
->headers(['ID', 'Name', 'Email'])
嵌套表头(支持任意深度)
->headers([ 'ID', 'Name', 'Contact' => ['Email', 'Phone'], 'Address' => ['City', 'Country' => ['Code', 'Name']], ])
表头样式
->headerColor('#0F4C81') // 背景色(自动设置白色字体) ->headerFontColor('#FFFFFF') // 自定义字体颜色 ->headerBold() // 加粗 ->headerItalic() // 斜体 ->headerFontSize(14) // 字号
Rows
接受任何 iterable:数组、Generator、Iterator、Laravel Collection / LazyCollection、Eloquent cursor()、Think-ORM cursor() 等。
// Eloquent cursor ->rows(User::cursor()) // Generator ->rows((function () { for ($i = 1; $i <= 100000; $i++) { yield ['id' => $i, 'name' => "User {$i}"]; } })()) // 普通数组 ->rows([ ['Alice', 'alice@example.com'], ['Bob', 'bob@example.com'], ])
Column Formats
按列字母指定格式:
->currency('D') // 1,234.56 ->date('E') // 2024-01-15 ->datetime('F') // 2024-01-15 14:30:00 ->percentage('G') // 85.5% ->number('H') // 1,234 // 或批量指定 ->columnFormat([ 'D' => 'currency', 'E' => 'date', ])
Freeze
->freezeHeader() // 冻结表头行 ->freeze('B3') // 冻结到 B3 单元格 ->freezeColumn('A') // 冻结 A 列 ->freezeRow(5) // 冻结前 5 行
Filter
->filter() // 在表头行启用自动筛选
Column Width
// 手动指定 ->columnWidth(['A' => 5, 'B' => 30, 'C' => 40]) // 自动估算(默认 15 字符宽) ->autoWidth()
Merge
->merge('A1:C1') ->merge('D2:D5')
Body Style
->font('Calibri') // 字体族 ->fontSize(11) // 正文字号 ->alignCenter() // 水平居中 ->alignLeft() // 左对齐 ->alignRight() // 右对齐 ->wrapText() // 自动换行
Row Style
->rowHeight(25) // 行高 ->alternateColor('#F5F5F5') // 隔行背景色
Border
->border() // 全部边框(= borderAll) ->borderHeader() // 仅表头边框 ->borderBody() // 仅正文边框 ->borderAll() // 全部
Images
->image('A1', '/path/to/logo.png') ->image('B2', '/path/to/photo.jpg', 100, 80)
Title Block
->setTitle('Sales Report 2024') ->setSubtitle('Q4 Summary') ->setDescription('Generated by StreamXlsx')
Multiple Sheets
XlswriterBuilder::make() ->headers(['Name', 'Email']) ->rows(User::where('active', true)->cursor()) ->headerColor('#0F4C81') ->freezeHeader() ->addSheet('Inactive', function ($sheet) { $sheet->headers(['Name', 'Email']) ->rows(User::where('active', false)->cursor()) ->headerColor('#CC0000'); }) ->download('users.xlsx');
Style Callback
->style(function ($row, $index) { // 根据行数据动态返回样式(简化支持) if ($row['status'] === 'failed') { return ['backgroundColor' => '#FFCCCC']; } return null; })
Migration from SpreedCore
StreamXlsx 的 Fluent API 与 SpreedCore 的 SpreadsheetBuilder 完全兼容。迁移只需两步:
1. 替换入口
// 之前 use SpreeCore\Spreadsheet\SpreadsheetBuilder; $builder = SpreadsheetBuilder::make(); // 之后 use StreamXlsx\Engine\XlswriterBuilder; $builder = XlswriterBuilder::make();
2. 其余代码不变
所有 ->headers()、->rows()、->headerColor()、->freezeHeader()、->download() 等链式调用保持一致。
不支持的功能
以下 SpreedCore 功能在 StreamXlsx 中不可用(受限于 xlswriter 能力):
| 功能 | 原因 |
|---|---|
| Logo 自动下载 | xlswriter 无法下载远程 URL 图片。请预下载到本地后用 ->image() |
| 复杂样式回调 | xlswriter 的格式化能力有限于 C 扩展提供的 Format API |
| ODS / PDF 导出 | xlswriter 仅支持 XLSX 和 CSV |
Architecture
XlswriterBuilder ← Fluent API 入口(与 SpreedCore API 兼容)
│
├── SheetBuilder ← 逐 Sheet 配置,产出 SheetDefinition
│
├── XlswriterAssembler ← 核心:遍历 SheetDefinition[],逐行直接写盘
│ │
│ ├── 标题块写入
│ ├── 表头写入(支持嵌套合并)
│ ├── 数据行流式写入 ← 数据源 → insertText() → 磁盘,不经内存模型
│ ├── 冻结 / 筛选 / 列宽 / 合并 / 图片
│ └── 产出 ExportResult(文件路径 + MIME)
│
└── OutputHandler ← 下载 / 存储 / 流式 / 原始(与 SpreedCore 完全复用)
数据流:
DB cursor → Generator → RowSourceAdapterFactory → XlswriterAssembler → insertText() → 磁盘
↑ 逐行写入
↑ 无内存积累
Performance
测试环境:PHP 8.2, 16GB RAM, SSD, 10 列 × N 行随机数据。
| 行数 | SpreedCore (PhpSpreadsheet) | StreamXlsx (xlswriter) | 内存差距 |
|---|---|---|---|
| 1,000 | 8MB / 0.3s | 2MB / 0.1s | 4× |
| 10,000 | 48MB / 2.5s | 2.5MB / 0.4s | 19× |
| 50,000 | 240MB / 12s | 2.8MB / 1.5s | 86× |
| 100,000 | 480MB / 25s | 3MB / 3s | 160× |
| 500,000 | OOM 💀 | 3.5MB / 15s | ∞ |
License
MIT
[快速开始](2-quick-start)
[架构概述](4-architecture-overview)
[SpreadsheetBuilder 入口](5-spreadsheetbuilder-entry-point)
[输出处理器](12-output-handlers)