PHPackages                             moshontong/excel-export-streamer - PHPackages - PHPackages  [Skip to content](#main-content)[PHPackages](/)[Directory](/)[Categories](/categories)[Trending](/trending)[Leaderboard](/leaderboard)[Changelog](/changelog)[Analyze](/analyze)[Collections](/collections)[Log in](/login)[Sign up](/register)

1. [Directory](/)
2. /
3. [PDF &amp; Document Generation](/categories/documents)
4. /
5. moshontong/excel-export-streamer

ActiveLibrary[PDF &amp; Document Generation](/categories/documents)

moshontong/excel-export-streamer
================================

A memory-efficient Laravel package for streaming Excel exports without holding data in memory

v1.1.0(8mo ago)2110↓50%MITPHPPHP ^8.1

Since Aug 12Pushed 8mo agoCompare

[ Source](https://github.com/moshOntong-IT/mosh-excel-export-streamer)[ Packagist](https://packagist.org/packages/moshontong/excel-export-streamer)[ Docs](https://github.com/moshOntong-IT/mosh-excel-export-streamer)[ RSS](/packages/moshontong-excel-export-streamer/feed)WikiDiscussions main Synced 1mo ago

READMEChangelogDependencies (3)Versions (3)Used By (0)

Mosh Excel Export Streamer
==========================

[](#mosh-excel-export-streamer)

[![Latest Version on Packagist](https://camo.githubusercontent.com/a90dba89ba7931fb15c0b2951e023ebc6be872c5fcabc57029829908984ea836/68747470733a2f2f696d672e736869656c64732e696f2f7061636b61676973742f762f6d6f73682f657863656c2d6578706f72742d73747265616d65722e7376673f7374796c653d666c61742d737175617265)](https://packagist.org/packages/mosh/excel-export-streamer)[![Total Downloads](https://camo.githubusercontent.com/11f2a182ef61b5386f085a913dd220947a571bbc614502c248175d2eca89a9a2/68747470733a2f2f696d672e736869656c64732e696f2f7061636b61676973742f64742f6d6f73682f657863656c2d6578706f72742d73747265616d65722e7376673f7374796c653d666c61742d737175617265)](https://packagist.org/packages/mosh/excel-export-streamer)[![License](https://camo.githubusercontent.com/12aebc5983f4dc9f0fcc5520ef8b378351a6b8515b498f07040ea79723819e51/68747470733a2f2f696d672e736869656c64732e696f2f7061636b61676973742f6c2f6d6f73682f657863656c2d6578706f72742d73747265616d65722e7376673f7374796c653d666c61742d737175617265)](https://packagist.org/packages/mosh/excel-export-streamer)

A memory-efficient Laravel package for exporting Excel files without running into memory limits. Features true streaming for CSV exports and memory-optimized file generation for XLSX exports. Perfect for exporting millions of records safely and efficiently.

Features
--------

[](#features)

- **Memory Efficient**: Uses different optimized approaches per format - true streaming for CSV, temporary files for XLSX
- **Flexible Data Sources**: Works with Eloquent queries, arrays, custom data providers, and data mapper callbacks
- **Multiple Formats**: Supports CSV and XLSX formats
- **Multi-Sheet Support**: Create XLSX files with multiple worksheets
- **Chunked Processing**: Processes data in configurable chunks with smart optimization
- **Framework Agnostic**: Use in any controller or service
- **Comprehensive Logging**: Built-in logging for debugging and monitoring
- **Performance Optimized**: Automatic query complexity detection and chunk size optimization
- **No External Dependencies**: Built with native PHP and Laravel

How It Works
------------

[](#how-it-works)

The package uses different approaches optimized for each export format:

### CSV Exports 🚀

[](#csv-exports-)

- **True Streaming**: Data streams directly from database to browser chunk-by-chunk
- **Immediate Download**: Download dialog appears instantly as data flows
- **Memory Efficient**: Never holds the full dataset in memory
- **Best For**: Large datasets where immediate streaming is priority

### XLSX Exports 📊

[](#xlsx-exports-)

- **Generate + Stream**: Creates complete XLSX file using temporary storage, then streams the file
- **Excel Compatible**: Produces valid ZIP-structured XLSX files that Excel opens correctly
- **Memory Optimized**: Uses chunked processing and temporary files to minimize memory usage
- **Best For**: Datasets requiring Excel compatibility, multi-sheet functionality

> **💡 Pro Tip**: Choose CSV for maximum performance and true streaming. Choose XLSX when Excel compatibility and formatting are required.

Installation
------------

[](#installation)

Install via Composer:

```
composer require mosh/excel-export-streamer
```

The package will auto-register via Laravel's package discovery.

### Publish Configuration (Optional)

[](#publish-configuration-optional)

```
php artisan vendor:publish --tag=excel-export-streamer-config
```

Basic Usage
-----------

[](#basic-usage)

### 1. Inject the Service

[](#1-inject-the-service)

```
use Mosh\ExcelExportStreamer\Services\ExcelStreamExporter;

class UserController extends Controller
{
    public function export(ExcelStreamExporter $exporter)
    {
        return $exporter->streamFromQuery(
            User::query(),
            ['name', 'email', 'created_at'],
            'users-export.csv'
        );
    }
}
```

### 2. Export from Eloquent Query

[](#2-export-from-eloquent-query)

```
public function exportUsers(ExcelStreamExporter $exporter)
{
    $query = User::where('active', true)
                 ->with('profile')
                 ->orderBy('created_at');

    return $exporter->streamFromQuery(
        $query,
        ['name', 'email', 'profile.phone', 'created_at'],
        'active-users.xlsx', // XLSX: generates file first, then streams
        ['format' => 'xlsx', 'chunk_size' => 500]
    );
}

// For true streaming, use CSV format:
public function exportUsersStreaming(ExcelStreamExporter $exporter)
{
    return $exporter->streamFromQuery(
        User::where('active', true)->orderBy('created_at'),
        ['name', 'email', 'created_at'],
        'active-users.csv', // CSV: streams immediately as data is processed
        ['format' => 'csv', 'chunk_size' => 2000]
    );
}
```

### 3. Export from Array

[](#3-export-from-array)

```
public function exportCustomData(ExcelStreamExporter $exporter)
{
    $data = [
        ['name' => 'John', 'email' => 'john@example.com'],
        ['name' => 'Jane', 'email' => 'jane@example.com'],
    ];

    $headers = ['Name', 'Email'];

    return $exporter->streamFromArray($data, $headers, 'custom-data.csv');
}
```

### 4. Advanced Usage with Custom Data Provider

[](#4-advanced-usage-with-custom-data-provider)

```
use Mosh\ExcelExportStreamer\Contracts\DataProviderInterface;

class CustomDataProvider implements DataProviderInterface
{
    public function getDataChunks(int $chunkSize = 1000): Generator
    {
        // Your custom data chunking logic
        for ($i = 0; $i < 10000; $i += $chunkSize) {
            yield $this->fetchDataChunk($i, $chunkSize);
        }
    }

    public function getTotalCount(): ?int
    {
        return 10000; // Optional for progress tracking
    }

    public function getHeaders(): array
    {
        return ['id', 'name', 'value'];
    }
}

// Usage
public function exportCustom(ExcelStreamExporter $exporter)
{
    $provider = new CustomDataProvider();
    return $exporter->streamFromProvider($provider, 'custom-export.xlsx');
}
```

### 5. Data Mapper for Complex Transformations

[](#5-data-mapper-for-complex-transformations)

Transform data row-by-row during streaming with custom callbacks. Perfect for complex calculations, relationship data, and custom formatting:

```
public function exportOrdersWithCalculations(ExcelStreamExporter $exporter)
{
    $query = Order::with(['customer', 'items.product'])
                  ->where('status', 'completed')
                  ->orderBy('created_at');

    $headers = ['Order #', 'Customer', 'Total Items', 'Revenue', 'Profit Margin', 'Status'];

    return $exporter->streamFromQuery(
        $query,
        $headers,
        'orders-with-calculations.xlsx',
        ['format' => 'xlsx', 'chunk_size' => 500],
        function($order) {
            // Complex transformations applied per record during streaming
            $totalRevenue = $order->items->sum(fn($item) => $item->quantity * $item->price);
            $totalCost = $order->items->sum(fn($item) => $item->quantity * $item->product->cost);
            $profitMargin = $totalRevenue > 0 ? (($totalRevenue - $totalCost) / $totalRevenue) * 100 : 0;

            return [
                $order->order_number,
                $order->customer->name,
                $order->items->count(),
                number_format($totalRevenue, 2),
                number_format($profitMargin, 1) . '%',
                ucfirst($order->status)
            ];
        }
    );
}
```

**Data Mapper Benefits:**

- 🚀 **Memory Efficient**: Transforms data row-by-row during streaming (no memory bloat)
- 🔧 **Flexible**: Handle complex calculations, relationships, and custom formatting
- 🛡️ **Error Resilient**: Automatic fallback to default column extraction on mapper errors
- 🔄 **Backward Compatible**: Optional parameter, existing code continues working

**Advanced Example - Financial Report:**

```
public function exportFinancialReport(ExcelStreamExporter $exporter)
{
    $query = Account::with(['transactions', 'category'])
                   ->where('active', true)
                   ->orderBy('account_code');

    return $exporter->streamFromQuery(
        $query,
        ['Code', 'Name', 'Category', 'Balance', 'Last Transaction', 'Status'],
        'financial-report.csv', // CSV for maximum streaming performance
        ['format' => 'csv', 'chunk_size' => 2000],
        function($account) {
            $balance = $account->transactions->sum('amount');
            $lastTransaction = $account->transactions->sortByDesc('created_at')->first();

            return [
                $account->account_code,
                $account->name,
                $account->category->name ?? 'Uncategorized',
                '$' . number_format($balance, 2),
                $lastTransaction ? $lastTransaction->created_at->format('Y-m-d') : 'Never',
                $balance >= 0 ? 'Positive' : 'Negative'
            ];
        }
    );
}
```

### 6. Multi-Sheet XLSX Export

[](#6-multi-sheet-xlsx-export)

Create XLSX files with multiple worksheets in a single file:

#### Basic Multi-Sheet Export

[](#basic-multi-sheet-export)

```
public function exportMultiSheet(ExcelStreamExporter $exporter)
{
    $sheets = [
        'Active Users' => [
            'query' => User::where('active', true),
            'columns' => ['name', 'email', 'created_at']
        ],
        'Products' => [
            'query' => Product::where('in_stock', true),
            'columns' => ['name', 'price', 'stock_quantity']
        ],
        'Recent Orders' => [
            'query' => Order::where('created_at', '>=', now()->subDays(30)),
            'columns' => ['order_number', 'customer_name', 'total', 'status']
        ]
    ];

    // Note: Multi-sheet exports generate complete file first, then stream
    // This ensures proper XLSX structure but requires temporary storage
    return $exporter->streamWrapAsSheets($sheets, 'multi-report.xlsx');
}
```

#### Advanced Multi-Sheet with Data Mappers 🎯

[](#advanced-multi-sheet-with-data-mappers-)

Combine multi-sheet functionality with data transformation for powerful business reports:

```
public function exportAdvancedMultiSheet(ExcelStreamExporter $exporter)
{
    $sheets = [
        // Sheet 1: Customer Summary with Financial Calculations
        'Customer Summary' => [
            'query' => User::with(['orders'])->whereHas('orders'),
            'columns' => ['Name', 'Email', 'Orders Count', 'Total Spent', 'Avg Order Value', 'Status'],
            'options' => [
                'chunk_size' => 500,
                'data_mapper' => function($customer) {
                    $orders = $customer->orders;
                    $totalSpent = $orders->sum('total_amount');
                    $avgOrderValue = $orders->avg('total_amount');

                    return [
                        $customer->name,
                        $customer->email,
                        $orders->count(),
                        '$' . number_format($totalSpent, 2),
                        '$' . number_format($avgOrderValue ?? 0, 2),
                        $totalSpent > 1000 ? 'VIP' : ($totalSpent > 500 ? 'Regular' : 'New')
                    ];
                }
            ]
        ],

        // Sheet 2: Product Performance Analytics
        'Product Performance' => [
            'query' => Product::with(['orderItems'])->where('is_active', true),
            'columns' => ['Product', 'SKU', 'Price', 'Units Sold', 'Revenue', 'Performance Rating'],
            'options' => [
                'chunk_size' => 1000,
                'data_mapper' => function($product) {
                    $orderItems = $product->orderItems;
                    $totalSold = $orderItems->sum('quantity');
                    $revenue = $orderItems->sum(fn($item) => $item->quantity * $item->price);

                    $performance = 'Low';
                    if ($totalSold > 100) $performance = 'High';
                    elseif ($totalSold > 50) $performance = 'Medium';

                    return [
                        $product->name,
                        $product->sku,
                        '$' . number_format($product->price, 2),
                        $totalSold,
                        '$' . number_format($revenue, 2),
                        $performance
                    ];
                }
            ]
        ],

        // Sheet 3: Basic Orders (No Data Mapper - Direct Column Export)
        'Recent Orders' => [
            'query' => Order::where('created_at', '>=', now()->subDays(30))
                           ->orderBy('created_at', 'desc'),
            'columns' => ['order_number', 'status', 'total_amount', 'created_at']
        ],

        // Sheet 4: Complex Financial Report
        'Financial Analysis' => [
            'query' => Order::with(['user', 'orderItems.product'])
                           ->where('status', 'completed')
                           ->where('created_at', '>=', now()->subMonth()),
            'columns' => ['Order #', 'Customer', 'Items', 'Revenue', 'Est. Cost', 'Profit', 'Margin %'],
            'options' => [
                'chunk_size' => 300,
                'data_mapper' => function($order) {
                    $items = $order->orderItems;
                    $revenue = $items->sum(fn($item) => $item->quantity * $item->price);
                    $cost = $items->sum(fn($item) => $item->quantity * ($item->product->cost ?? $item->price * 0.6));
                    $profit = $revenue - $cost;
                    $margin = $revenue > 0 ? ($profit / $revenue) * 100 : 0;

                    return [
                        $order->order_number,
                        $order->user->name,
                        $items->count(),
                        '$' . number_format($revenue, 2),
                        '$' . number_format($cost, 2),
                        '$' . number_format($profit, 2),
                        number_format($margin, 1) . '%'
                    ];
                }
            ]
        ]
    ];

    return $exporter->streamWrapAsSheets($sheets, 'advanced-business-report.xlsx');
}
```

**Multi-Sheet Data Mapper Features:**

- 🎯 **Per-Sheet Transformation**: Each sheet can have its own data mapper with unique business logic
- 📊 **Mixed Sheet Types**: Combine sheets with data mappers and basic column exports in the same file
- 🚀 **Memory Efficient**: Data mappers work seamlessly with chunked processing for large datasets
- 🛡️ **Error Resilient**: Automatic fallback to column extraction if mapper fails on any sheet
- ⚙️ **Flexible Options**: Custom chunk sizes and options per sheet

Logging
-------

[](#logging)

The package includes comprehensive logging capabilities:

```
// Enable logging in config
'logging' => [
    'enabled' => true,
    'log_exports' => true,
    'log_chunks' => false, // Enable for detailed chunk processing logs
    'log_memory_warnings' => true,
    'performance_enabled' => true,
]
```

Log entries include:

- Export start/completion with record counts
- Memory usage and performance metrics
- Execution time warnings
- Query complexity detection
- Error tracking with context

Configuration
-------------

[](#configuration)

The package comes with sensible defaults, but you can customize everything:

```
// config/excel-export-streamer.php
return [
    'default_chunk_size' => 1000,

    'memory' => [
        'max_chunk_size' => 5000,
        'min_chunk_size' => 100,
        'auto_adjust_chunks' => true,
    ],

    'formats' => [
        'csv' => [
            'delimiter' => ',',
            'enclosure' => '"',
            'escape' => '\\',
        ],
        'xlsx' => [
            'memory_limit' => '1G',
            'temp_dir' => null,
        ],
    ],

    'filename' => [
        'include_timestamp' => true,
        'sanitize_filename' => true,
    ],

    'performance' => [
        'disable_query_log' => true,
        'gc_collect_cycles' => true,
        'memory_threshold' => 0.8,
        'max_execution_time' => 300,
        'auto_detect_query_complexity' => true,
        'complex_query_chunk_size' => 500,
        'simple_query_chunk_size' => 2000,
    ],

    'logging' => [
        'enabled' => true,
        'log_exports' => true,
        'log_chunks' => false,
        'log_memory_warnings' => true,
        'performance_enabled' => true,
    ],
];
```

Model Integration
-----------------

[](#model-integration)

Make your Eloquent models exportable:

```
use Mosh\ExcelExportStreamer\Contracts\ExportableInterface;

class User extends Model implements ExportableInterface
{
    public function getExportColumns(): array
    {
        return ['id', 'name', 'email', 'created_at'];
    }

    public function getExportHeaders(): array
    {
        return ['ID', 'Name', 'Email', 'Created At'];
    }

    public function transformForExport(): array
    {
        return [
            'id' => $this->id,
            'name' => $this->name,
            'email' => $this->email,
            'created_at' => $this->created_at->format('Y-m-d H:i:s'),
        ];
    }
}
```

Frontend Integration Examples
-----------------------------

[](#frontend-integration-examples)

### Vanilla JavaScript

[](#vanilla-javascript)

```
function downloadExport() {
  window.location.href = "/export/users";
}

// With fetch for better error handling
async function downloadExport() {
  try {
    const response = await fetch("/export/users");
    const blob = await response.blob();
    const url = window.URL.createObjectURL(blob);
    const a = document.createElement("a");
    a.href = url;
    a.download = "users-export.csv";
    a.click();
    window.URL.revokeObjectURL(url);
  } catch (error) {
    console.error("Export failed:", error);
  }
}
```

### Vue.js Example

[](#vuejs-example)

```

    {{ exporting ? "Exporting..." : "Export Users" }}

export default {
  data() {
    return {
      exporting: false,
    };
  },
  methods: {
    async exportUsers() {
      this.exporting = true;
      try {
        const response = await fetch("/export/users");
        const blob = await response.blob();
        const url = window.URL.createObjectURL(blob);
        const a = document.createElement("a");
        a.href = url;
        a.download = "users-export.csv";
        a.click();
        window.URL.revokeObjectURL(url);
      } catch (error) {
        alert("Export failed");
      } finally {
        this.exporting = false;
      }
    },
  },
};

```

Performance Tips
----------------

[](#performance-tips)

### Format Selection

[](#format-selection)

1. **Choose CSV for True Streaming**: For maximum performance and immediate download, use CSV format
2. **Use XLSX When Excel Compatibility Required**: Accept the trade-off of file generation for proper Excel support
3. **Consider Dataset Size**: CSV handles millions of records with constant memory usage; XLSX uses temporary files

### Optimization Strategies

[](#optimization-strategies)

4. **Optimize Chunk Size**: Start with 1000 for XLSX, 2000+ for CSV; adjust based on your data complexity
5. **Use Specific Columns**: Only select columns you need - especially important for XLSX temporary file size
6. **Add Database Indexes**: Ensure your queries are optimized for the columns you're ordering by
7. **Use Data Mappers for Complex Logic**: Instead of pre-processing data, use mapper callbacks for transformations during streaming
8. **Monitor Memory Usage**: Enable memory warnings in config to track usage patterns
9. **Enable Query Log Disabling**: Set `disable_query_log` to true in config for better performance

### XLSX-Specific Tips

[](#xlsx-specific-tips)

10. **Temporary Directory**: Configure fast storage (SSD) for `temp_dir` in XLSX config
11. **Cleanup Monitoring**: Large XLSX exports create temporary files - ensure adequate disk space

Error Handling
--------------

[](#error-handling)

```
use Mosh\ExcelExportStreamer\Exceptions\ExportException;

try {
    return $exporter->streamFromQuery($query, $columns, $filename);
} catch (ExportException $e) {
    return response()->json(['error' => $e->getMessage()], 400);
}
```

Testing
-------

[](#testing)

```
use Mosh\ExcelExportStreamer\Services\ExcelStreamExporter;

class ExportTest extends TestCase
{
    public function test_user_export()
    {
        $users = User::factory(10)->create();

        $exporter = app(ExcelStreamExporter::class);
        $response = $exporter->streamFromQuery(
            User::query(),
            ['name', 'email'],
            'test-export.csv'
        );

        $this->assertEquals(200, $response->getStatusCode());
        $this->assertStringContainsString('text/csv', $response->headers->get('Content-Type'));
    }
}
```

Requirements
------------

[](#requirements)

- PHP 8.1+
- Laravel 10.0+ or 11.0+

License
-------

[](#license)

MIT License. See LICENSE file for details.

Contributing
------------

[](#contributing)

Pull requests are welcome! Please ensure tests pass and follow PSR-12 coding standards.

Support
-------

[](#support)

- Create an issue on GitHub for bug reports
- Check existing issues before creating new ones
- Provide minimal reproduction examples

###  Health Score

34

—

LowBetter than 77% of packages

Maintenance62

Regular maintenance activity

Popularity15

Limited adoption so far

Community6

Small or concentrated contributor base

Maturity45

Maturing project, gaining track record

 Bus Factor1

Top contributor holds 100% of commits — single point of failure

How is this calculated?**Maintenance (25%)** — Last commit recency, latest release date, and issue-to-star ratio. Uses a 2-year decay window.

**Popularity (30%)** — Total and monthly downloads, GitHub stars, and forks. Logarithmic scaling prevents top-heavy scores.

**Community (15%)** — Contributors, dependents, forks, watchers, and maintainers. Measures real ecosystem engagement.

**Maturity (30%)** — Project age, version count, PHP version support, and release stability.

###  Release Activity

Cadence

Every ~0 days

Total

2

Last Release

269d ago

### Community

Maintainers

![](https://www.gravatar.com/avatar/3ea5ed5f0cd1038563c7019b3550b9ba570d3e554cd0b632c73e1ec3876e0e2b?d=identicon)[moshOntong-IT](/maintainers/moshOntong-IT)

---

Top Contributors

[![moshOntong-IT](https://avatars.githubusercontent.com/u/57244338?v=4)](https://github.com/moshOntong-IT "moshOntong-IT (5 commits)")

---

Tags

laravelexportstreamingexcelxlsxcsvmemory-efficientchunkinglarge-datasetmulti-sheet

###  Code Quality

TestsPHPUnit

### Embed Badge

![Health badge](/badges/moshontong-excel-export-streamer/health.svg)

```
[![Health](https://phpackages.com/badges/moshontong-excel-export-streamer/health.svg)](https://phpackages.com/packages/moshontong-excel-export-streamer)
```

###  Alternatives

[maatwebsite/excel

Supercharged Excel exports and imports in Laravel

12.7k144.3M709](/packages/maatwebsite-excel)[bfinlay/laravel-excel-seeder

Seed the database with Laravel using Excel, XLSX, XLS, CSV, ODS, Gnumeric, XML, HTML, SLK files

3944.4k](/packages/bfinlay-laravel-excel-seeder)[kolay/xlsx-stream

High-performance XLSX streaming writer for Laravel with zero disk I/O and direct S3 support

383.0k](/packages/kolay-xlsx-stream)

PHPackages © 2026

[Directory](/)[Categories](/categories)[Trending](/trending)[Changelog](/changelog)[Analyze](/analyze)
