Laravel 8 + Spatie MediaLibrary v10 生产环境生成响应式图片时依赖无法实例化的问题排查求助
各位开发者大佬,我现在遇到一个非常诡异的生产环境问题,想请大家帮忙分析排查:
我在 Laravel 8.0 搭配 Spatie\MediaLibrary v10 的项目中,尝试给接口上传的图片生成响应式版本,但生产环境一直抛出如下错误:
Target [Spatie\MediaLibrary\ResponsiveImages\WidthCalculator\WidthCalculator] is not instantiable while building [Spatie\MediaLibrary\ResponsiveImages\ResponsiveImageGenerator]
本地开发环境完全正常,而且我已经确认生产和本地的PHP版本都是8.1,必要的依赖库也都安装完整。更奇怪的是,图片本身能正常保存到目录中,只是响应式图片的相关额外文件(比如不同尺寸的变体、占位SVG)生成不出来。
我的代码结构说明
为了减少重复代码,我封装了一个SaveImagesClass,它的构造函数依赖ModelEventClass——这个类承载了从控制器注入的匿名函数,用来处理不同模型的保存逻辑(项目里有11个不同模块,都需要处理带图/不带图的数据保存,所以想做统一封装)。
相关服务绑定是在AppServiceProvider中完成的:
$this->app->bind(SaveImagesInterface::class, SaveImagesClass::class); $this->app->bind(SaveImagesClass::class, function ($app) { return new SaveImagesClass($app->make(ModelEventClass::class)); });
SaveImagesClass核心代码:
class SaveImagesClass implements SaveImagesInterface { use ResponseJsonTrait; public function __construct( protected ModelEventClass $modelEventClass ) {} public function store(Request $request, array $data): array { try { $ids = []; $user_id = auth()->user()->id; foreach ($data as $key => $value) { if(!$this->modelEventClass->exists($value, $user_id)) { $this->modelEventClass->insert($request, $value["data"], $chunk["image"]); $ids[] = $value["data"]["id"]; } } $msg = __("Saved"); return $this->resultArray($msg, 200, $ids); } catch(\Exception $e) { $msg = $e->getMessage(); return $this->resultArray($msg, 500, null, false); } } }
ModelEventClass代码:
class ModelEventClass { public function __construct( protected Closure $insert_model_data, protected Closure $model_data_exists ) {} public function insert(Request $request, array $data, ?string $image_name): void { $local = Closure::fromCallable($this->insert_model_data); $local($request, $data, $image_name); } public function exists(array $value, int $user_id): bool { $local = Closure::fromCallable($this->model_data_exists); return $local($value, $user_id); } }
控制器中的调用逻辑(注入匿名函数处理模型和图片):
public function __construct() { $this->saveImagesClass = new SaveImagesClass(new ModelEventClass( function(Request $request, array $data, ?string $image_name): void { $create = Model::create($data); if(isset($image_name)) { $uuid = (string) Str::uuid(); $create->addMediaFromRequest($image_name) ->setName($uuid) ->withResponsiveImages() // *就是这行代码在生产环境触发报错* ->toMediaCollection(); } }, function(array $value, int $user_id): bool { return Model::where("user_id", "=", $user_id) ->where("qr_id", "=", $value["qr_id"]) ->where("date", "=", $value["date"]) ->exists(); } )); }
我已经完成的排查步骤
- 移除封装直接在控制器保存:把图片保存逻辑直接写到控制器中,去掉
SaveImagesClass和ModelEventClass的封装层,结果错误依然存在。 - 手动绑定WidthCalculator依赖:查资料发现MediaLibrary有自带的服务提供者,里面注册了相关服务,不知道为什么生产环境没自动注册,于是我在
AppServiceProvider的register方法中添加:
这行代码解决了WidthCalculator的实例化错误,但又出现新的错误:$this->app->bind(WidthCalculator::class, config('media-library.responsive_images.width_calculator'));Target [Spatie\MediaLibrary\ResponsiveImages\TinyPlaceholderGenerator\TinyPlaceholderGenerator] is not instantiable while building [Spatie\MediaLibrary\ResponsiveImages\ResponsiveImageGenerator]
- 手动绑定占位生成器依赖:接着把占位生成器的绑定也添加到
AppServiceProvider中,这次响应式图片终于能生成了,但又出现新错误:No hint path defined for [media-library]
- 手动加载视图路径:尝试在
AppServiceProvider的boot方法中添加:
结果又抛出新的错误:$this->loadViewsFrom(__DIR__.'/../resources/views', 'media-library');View [placeholderSvg] not found
现在彻底卡在这里了,明明本地环境一切正常,生产环境的配置和依赖都和本地对齐了,为什么会出现这些连锁问题?有没有大佬能指点一下方向?
备注:内容来源于stack exchange,提问作者Hmerman6006

