spatie/laravel-medialibrary上传faker-images图片时出现URL无法访问错误
问题:Laravel Medialibrary 无法下载 Picsum 图片返回403错误
在Laravel 9项目中使用mmo/faker-images生成图片URL,再通过spatie/laravel-medialibrary上传时,抛出UnreachableUrl异常,提示URL无法访问,但该URL在浏览器中可正常打开。
代码示例
$faker = \Faker\Factory::create(); $faker->addProvider(new \Mmo\Faker\PicsumProvider($faker)); $faker->addProvider(new \Mmo\Faker\LoremSpaceProvider($faker)); $pageUploadedImageFile= $faker->picsumUrl(400, 400); $imageMedia = $this->modelItem ->addMediaFromUrl($pageUploadedImageFile) ->usingFileName($imageFilename) ->toMediaCollection('media');
生成的URL示例:
https://picsum.photos/400/400?random=83611
错误信息
Spatie\MediaLibrary\MediaCollections\Exceptions\UnreachableUrl Url `https://picsum.photos/400/400?random=83611` cannot be reached at vendor/spatie/laravel-medialibrary/src/MediaCollections/Exceptions/UnreachableUrl.php:9 5▕ class UnreachableUrl extends FileCannotBeAdded 6▕ { 7▕ public static function create(string $url): self 8▕ { ➜ 9▕ return new static("Url `{$url}` cannot be reached"); 10▕ } 11▕ } 12▕
项目依赖版本
"laravel/framework": "^9.26.1", "laravel/tinker": "^2.7.2", "spatie/laravel-medialibrary": "^10.4.4", "spatie/laravel-permission": "^5.5.5", "squizlabs/php_codesniffer": "*", "barryvdh/laravel-ide-helper": "^2.12", "fakerphp/faker": "^1.9.1", "laravel/sail": "^1.0.1", "mmo/faker-images": "^0.6.0", "mockery/mockery": "^1.4.4", "nunomaduro/collision": "^6.1"
调试发现
追踪源码后发现,错误来自Spatie\MediaLibrary\Downloaders\DefaultDownloader的getTempFile方法,使用fopen请求URL时返回403 Forbidden:
<?php namespace Spatie\MediaLibrary\Downloaders; use Spatie\MediaLibrary\MediaCollections\Exceptions\UnreachableUrl; class DefaultDownloader implements Downloader { public function getTempFile(string $url): string { \Log::info( ' getTempFile $url::'); \Log::info( $url); $stream = fopen($url, 'r'); \Log::info( ' getTempFile $stream::'); \Log::info( $stream); if (! $stream ) { throw UnreachableUrl::create($url); } $temporaryFile = tempnam(sys_get_temp_dir(), 'media-library'); file_put_contents($temporaryFile, $stream); return $temporaryFile; } }
日志输出:
[2022-09-29 04:10:51] local.INFO: getTempFile $url:: [2022-09-29 04:10:51] local.INFO: https://picsum.photos/800/600?random=86375 [2022-09-29 04:10:51] local.ERROR: fopen(https://picsum.photos/800/600?random=86375): Failed to open stream: HTTP request failed! HTTP/1.1 403 Forbidden {"userId":7,"exception":"[object] (ErrorException(code: 0): fopen(https://picsum.photos/800/600?random=86375): Failed to open stream: HTTP request failed! HTTP/1.1 403 Forbidden
原因分析
这不是Medialibrary的防护机制,而是Picsum.photos服务器会拦截没有User-Agent标识的请求。浏览器请求时会自动带上User-Agent,所以能正常访问;而fopen默认不带任何请求头,因此被服务器拒绝,返回403。
解决方案
方案1:自定义下载器,添加User-Agent请求头
创建自定义下载器类,使用带请求头的方式获取文件:
<?php namespace App\Services\MediaLibrary; use Spatie\MediaLibrary\Downloaders\Downloader; use Spatie\MediaLibrary\MediaCollections\Exceptions\UnreachableUrl; class CustomDownloader implements Downloader { public function getTempFile(string $url): string { $context = stream_context_create([ 'http' => [ 'header' => 'User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/105.0.0.0 Safari/537.36' ] ]); $stream = fopen($url, 'r', false, $context); if (! $stream) { throw UnreachableUrl::create($url); } $temporaryFile = tempnam(sys_get_temp_dir(), 'media-library'); file_put_contents($temporaryFile, $stream); return $temporaryFile; } }
然后在AppServiceProvider的boot方法中注册自定义下载器:
use Spatie\MediaLibrary\MediaCollections\Downloaders\Downloader; use App\Services\MediaLibrary\CustomDownloader; public function boot() { $this->app->bind(Downloader::class, CustomDownloader::class); }
方案2:直接生成本地临时文件再上传
使用mmo/faker-images的picsum方法直接生成本地临时图片,再通过addMedia上传:
// 生成本地临时图片 $tempImage = $faker->picsum(storage_path('app/temp'), 400, 400); // 上传到媒体库 $imageMedia = $this->modelItem ->addMedia($tempImage) ->usingFileName($imageFilename) ->toMediaCollection('media'); // 可选:删除临时文件 unlink($tempImage);
这个方法绕开了远程URL下载,直接操作本地文件,更稳定。
内容的提问来源于stack exchange,提问作者Petro Gromovo
相关产品推荐
相关产品推荐

