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

GitLab CI中PHPUnit测试偶发CouldNotLoadImage异常求助

GitLab CI中PHPUnit测试偶尔失败:Spatie Media Library无法加载临时图片

异常信息

Spatie\Image\Exceptions\CouldNotLoadImage: Could not load image at path `/builds/api/storage/media-library/temp/u9hn06FL7CIz4gDf3PClKfiCsP6CCNEn/eKb9zA0BzpXjZg1CDSityXjDUcewN0yfthumb-xs.jpeg`
/builds/api/vendor/spatie/image/src/Exceptions/CouldNotLoadImage.php:11
/builds/api/vendor/spatie/image/src/Drivers/Gd/GdDriver.php:92
/builds/api/vendor/spatie/image/src/Image.php:49
/builds/api/vendor/spatie/laravel-medialibrary/src/Conversions/Actions/PerformManipulationsAction.php:34
/builds/api/vendor/spatie/laravel-medialibrary/src/Conversions/Actions/PerformConversionAction.php:30
/builds/api/vendor/spatie/laravel-medialibrary/src/Conversions/FileManipulator.php:71
/builds/api/vendor/laravel/framework/src/Illuminate/Collections/Traits/EnumeratesValues.php:240
/builds/api/vendor/spatie/laravel-medialibrary/src/Conversions/FileManipulator.php:70
/builds/api/vendor/spatie/laravel-medialibrary/src/Conversions/Jobs/PerformConversionsJob.php:30
/builds/api/vendor/laravel/framework/src/Illuminate/Container/BoundMethod.php:36
/builds/api/vendor/laravel/framework/src/Illuminate/Container/Util.php:41
/builds/api/vendor/laravel/framework/src/Illuminate/Container/BoundMethod.php:93
/builds/api/vendor/laravel/framework/src/Illuminate/Container/BoundMethod.php:35
/builds/api/vendor/laravel/framework/src/Illuminate/Container/Container.php:662
/builds/api/vendor/laravel/framework/src/Illuminate/Bus/Dispatcher.php:128
/builds/api/vendor/laravel/framework/src/Illuminate/Pipeline/Pipeline.php:144
/builds/api/vendor/laravel/framework/src/Illuminate/Pipeline/Pipeline.php:119
/builds/api/vendor/laravel/framework/src/Illuminate/Bus/Dispatcher.php:132
/builds/api/vendor/laravel/framework/src/Illuminate/Queue/CallQueuedHandler.php:123
/builds/api/vendor/laravel/framework/src/Illuminate/Pipeline/Pipeline.php:144
/builds/api/vendor/laravel/framework/src/Illuminate/Pipeline/Pipeline.php:119
/builds/api/vendor/laravel/framework/src/Illuminate/Queue/CallQueuedHandler.php:122
/builds/api/vendor/laravel/framework/src/Illuminate/Queue/CallQueuedHandler.php:70
/builds/api/vendor/laravel/framework/src/Illuminate/Queue/Jobs/Job.php:102
/builds/api/vendor/laravel/framework/src/Illuminate/Queue/SyncQueue.php:43
/builds/api/vendor/laravel/framework/src/Illuminate/Queue/Queue.php:59
/builds/api/vendor/laravel/framework/src/Illuminate/Bus/Dispatcher.php:247
/builds/api/vendor/laravel/framework/src/Illuminate/Bus/Dispatcher.php:230
/builds/api/vendor/laravel/framework/src/Illuminate/Bus/Dispatcher.php:77
/builds/api/vendor/laravel/framework/src/Illuminate/Foundation/Bus/PendingDispatch.php:193
/builds/api/vendor/laravel/framework/src/Illuminate/Foundation/helpers.php:388
/builds/api/vendor/spatie/laravel-medialibrary/src/Conversions/FileManipulator.php:98
/builds/api/vendor/spatie/laravel-medialibrary/src/Conversions/FileManipulator.php:40
/builds/api/vendor/spatie/laravel-medialibrary/src/MediaCollections/Filesystem.php:37
/builds/api/vendor/spatie/laravel-medialibrary/src/MediaCollections/FileAdder.php:438
/builds/api/vendor/spatie/laravel-medialibrary/src/MediaCollections/FileAdder.php:420
/builds/api/vendor/spatie/laravel-medialibrary/src/MediaCollections/FileAdder.php:339
/builds/api/database/factories/ProjectPool/ProjectFactory.php:88
/builds/api/vendor/laravel/framework/src/Illuminate/Database/Eloquent/Factories/Factory.php:713
/builds/api/vendor/laravel/framework/src/Illuminate/Collections/Traits/EnumeratesValues.php:240
/builds/api/vendor/laravel/framework/src/Illuminate/Database/Eloquent/Factories/Factory.php:712
/builds/api/vendor/laravel/framework/src/Illuminate/Collections/Traits/EnumeratesValues.php:240
/builds/api/vendor/laravel/framework/src/Illuminate/Database/Eloquent/Factories/Factory.php:711
/builds/api/vendor/laravel/framework/src/Illuminate/Database/Eloquent/Factories/Factory.php:289
/builds/api/tests/Feature/API/Mutations/ProjectPool/UpdateProjectTest.php:107

已知环境与问题特征

  • 本地测试无异常,GitLab CI流水线偶尔出现失败,无固定失败用例
  • 本地和CI环境均使用Minio作为Laravel文件存储磁盘
  • Spatie Laravel Media Library版本:v11.4.7
  • 推测核心原因:文件从Minio复制到本地临时目录失败,但组件未校验复制结果

排查与解决步骤

1. 增加文件复制后的校验逻辑

重写Media Library的Filesystem类中的copyFromRemote方法,添加本地文件存在性与可读性校验:

use Spatie\MediaLibrary\MediaCollections\Filesystem as BaseFilesystem;

class CustomFilesystem extends BaseFilesystem
{
    public function copyFromRemote(string $sourcePath, string $targetPath): void
    {
        parent::copyFromRemote($sourcePath, $targetPath);
        
        // 校验文件是否存在且可读
        if (!file_exists($targetPath) || !is_readable($targetPath)) {
            throw new \Exception("Failed to copy file from Minio to local path: {$targetPath}");
        }
    }
}

然后在AppServiceProvider中替换绑定:

public function register()
{
    $this->app->bind(\Spatie\MediaLibrary\MediaCollections\Filesystem::class, CustomFilesystem::class);
}

2. 调整CI环境临时目录权限

在GitLab CI脚本中添加权限配置,确保测试进程对临时目录有读写权限:

before_script:
  - chmod -R 775 /builds/api/storage/media-library/temp
  - chown -R gitlab-runner:gitlab-runner /builds/api/storage/media-library/temp

3. 切换测试环境队列驱动

将同步队列改为数组队列,避免文件未完全复制就触发转换任务:
在phpunit.xml中修改:

<env name="QUEUE_CONNECTION" value="array"/>

或者在测试类中临时切换:

use Illuminate\Support\Facades\Queue;

protected function setUp(): void
{
    parent::setUp();
    Queue::fake();
}

4. 优化Minio客户端配置

在config/filesystems.php的Minio磁盘配置中增加超时与重试机制:

'disks' => [
    'minio' => [
        'driver' => 's3',
        'key' => env('MINIO_ACCESS_KEY'),
        'secret' => env('MINIO_SECRET_KEY'),
        'region' => env('MINIO_REGION'),
        'bucket' => env('MINIO_BUCKET'),
        'url' => env('MINIO_URL'),
        'endpoint' => env('MINIO_ENDPOINT'),
        'use_path_style_endpoint' => true,
        'options' => [
            'timeout' => 30,
            'connect_timeout' => 10,
            'retries' => 3,
        ],
    ],
],

5. 启用详细日志排查

在CI环境中开启Laravel debug日志,同时在测试代码中添加文件状态日志:

// 在Factory或者测试类中添加
\Log::info('Copying file to temp', [
    'source' => $sourcePath,
    'target' => $targetPath,
    'target_exists' => file_exists($targetPath),
    'target_readable' => is_readable($targetPath),
]);

内容的提问来源于stack exchange,提问作者Christian Holländer

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.24 07:13:11