You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

使用maatwebsite/excel导入400MB大Excel文件失败求助

大Excel文件导入内存溢出问题解决

问题背景

需通过Laravel Artisan命令导入400MB的Excel文件,文件包含50万行、300列,存储在public目录下。已尝试调整memory_limit至1500M/2500M,且将导入流程加入队列,但仍出现内存分配失败或进程被自动杀死的问题。

Artisan命令代码

public function handle()
{
    $this->output->title('Excel Import Process Start');

    // Check type is valid
    $file = $this->option('file');

    $filePath = public_path('excel-import/' . $file);
    if (!File::exists($filePath)) {
        return $this->output->error("File is not found!");
    }

    Log::info("step 1");
    try {
        (new ImportApplications)->withOutput($this->output)->import($filePath);
    } catch (Exception $e) {
        $this->output->error("Import Exception");
        $this->output->info($e->getMessage());
        return false;
    }

    $this->output->success('Import successful');
    return true;
}

导入类代码

class ImportApplications implements ToModel, WithHeadingRow, WithBatchInserts, WithChunkReading, WithUpserts, WithProgressBar , ShouldQueue
{
    use Importable;
    /**
     * @param array $row
     *
     * @return \Illuminate\Database\Eloquent\Model|null
     */

    public function model(array $row)
    {
        Log::info($row);
        return new Application([
            'FILED_NAME' => $row['column_name'],
            ...
        ]);
    }

    public function batchSize(): int
    {
        return 1000;
    }

    public function chunkSize(): int
    {
        return 1000;
    }
}

遇到的错误

  • 设置memory_limit: 1500M时,报错:

XMLReader::read(): Memory allocation failed : growing input buffer

  • 设置memory_limit: 2500M时,进程被自动杀死
  • 加入队列处理后问题依旧

解决方案

1. 过滤不必要的列,减少每行数据量

添加WithReadFilter接口,仅读取业务需要的列,避免加载300列的全部数据:

use Maatwebsite\Excel\Concerns\WithReadFilter;
use Maatwebsite\Excel\Readers\ReadFilter;

class ImportApplications implements ToModel, WithHeadingRow, WithBatchInserts, WithChunkReading, WithUpserts, WithProgressBar, ShouldQueue, WithReadFilter
{
    use Importable;

    // 定义需要导入的列名(根据实际业务调整)
    private $requiredColumns = ['column_name', 'target_column_1', 'target_column_2'];

    public function readFilter(): ReadFilter
    {
        return new class($this->requiredColumns) implements ReadFilter {
            private $columns;

            public function __construct($columns)
            {
                $this->columns = $columns;
            }

            public function acceptRow($rowIndex)
            {
                return true; // 保留所有行,如需跳过特定行可在此处理
            }

            public function acceptColumn($columnIndex, $columnLetter)
            {
                // 仅保留需要的列
                return in_array($columnLetter, array_flip($this->columns));
            }
        };
    }

    // 其他原有方法保持不变
}

2. 降低Chunk和Batch大小

当前1000行的批次对于300列数据来说内存占用过高,建议降低到200-500行:

public function batchSize(): int
{
    return 200;
}

public function chunkSize(): int
{
    return 200;
}

3. 关闭逐行日志输出

代码中Log::info($row)会将每行数据写入日志,极大消耗内存与IO资源,直接移除该行:

public function model(array $row)
{
    // Log::info($row); // 移除该行日志
    return new Application([
        'FILED_NAME' => $row['column_name'],
        ...
    ]);
}

4. 优化队列进程配置

若使用队列,需单独配置队列进程的内存限制(以Supervisor为例),在配置文件中添加:

php_admin_value[memory_limit] = 1024M

同时延长队列超时时间,避免导入中途超时:

php artisan queue:work --timeout=3600

5. 改用批量插入替代模型实例化

放弃ToModel接口,使用ToCollection手动处理批量插入,减少模型实例化的内存开销:

class ImportApplications implements ToCollection, WithHeadingRow, WithChunkReading, WithProgressBar, ShouldQueue, WithReadFilter
{
    use Importable;

    private $requiredColumns = ['column_name', 'target_column_1', 'target_column_2'];

    public function readFilter(): ReadFilter
    {
        // 同上述过滤器实现
    }

    public function chunkSize(): int
    {
        return 200;
    }

    public function collection(Collection $rows)
    {
        $insertData = $rows->map(function ($row) {
            return [
                'FILED_NAME' => $row['column_name'],
                // 其他字段映射
            ];
        })->toArray();

        // 使用批量插入/更新,避免逐个模型保存
        Application::upsert($insertData, ['unique_key'], ['update_column_1', 'update_column_2']);
    }
}

6. 系统层面优化

  • 检查服务器空闲内存,避免因内存不足被OOM Killer杀死进程(可通过dmesg日志确认)
  • 调整PHP配置:max_execution_time = 3600、max_input_time = 3600,确保导入有足够执行时间

内容的提问来源于stack exchange,提问作者Harshil Patanvadiya

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.21 20:23:08