diff --git a/src/CollectionExtension.php b/src/CollectionExtension.php index efe680f..a268d6f 100644 --- a/src/CollectionExtension.php +++ b/src/CollectionExtension.php @@ -49,6 +49,11 @@ class CollectionExtension return fn ($callback) => $this->registerCustomCallback('withFallback', $callback); } + public function maxWidth() + { + return fn ($callback) => $this->registerCustomCallback('maxWidth', $callback); + } + public function runCallback() { return function (string $callback, ...$parameters) { @@ -76,6 +81,7 @@ class CollectionExtension } $this->customCallbacks = collect([ 'forceFileName' => fn ($model, $name) => $name, + 'maxWidth' => fn ($size) => null, 'stored' => fn ($event) => true, 'after' => fn ($event) => true, 'destroyed' => fn ($event) => true, diff --git a/src/MediaController.php b/src/MediaController.php index 50c944d..a95eb46 100644 --- a/src/MediaController.php +++ b/src/MediaController.php @@ -7,8 +7,11 @@ use Illuminate\Http\JsonResponse; use Illuminate\Http\Request; use Illuminate\Validation\Rule; use Illuminate\Validation\ValidationException; +use Spatie\Image\Image; use Spatie\LaravelData\DataCollection; use Spatie\MediaLibrary\HasMedia; +use Spatie\MediaLibrary\MediaCollections\Exceptions\InvalidBase64Data; +use Spatie\MediaLibrary\MediaCollections\FileAdder; use Spatie\MediaLibrary\MediaCollections\MediaCollection; use Spatie\MediaLibrary\MediaCollections\Models\Media; @@ -46,8 +49,8 @@ class MediaController $pathinfo = pathinfo($c['name']); $basename = $collection->runCallback('forceFileName', $model, $pathinfo['filename']); $path = $basename.'.'.$pathinfo['extension']; - $adder = $model - ->addMediaFromBase64($c['content']) + + $adder = $this->fileAdderFromData($model, $c['content'], $collection) ->usingName($basename) ->usingFileName($path) ->withCustomProperties($collection->runCallback('withDefaultProperties', $path)); @@ -133,4 +136,34 @@ class MediaController return $model; } + + protected function fileAdderFromData($model, $data, $collection): FileAdder + { + $maxWidth = $collection->runCallback('maxWidth', 9); + if (str_contains($data, ';base64')) { + [$_, $data] = explode(';', $data); + [$_, $data] = explode(',', $data); + } + + // strict mode filters for non-base64 alphabet characters + $binaryData = base64_decode($data, true); + + if (false === $binaryData) { + throw InvalidBase64Data::create(); + } + + // decoding and then reencoding should not change the data + if (base64_encode($binaryData) !== $data) { + throw InvalidBase64Data::create(); + } + + $tmpFile = tempnam(sys_get_temp_dir(), 'media-library'); + file_put_contents($tmpFile, $binaryData); + + if (null !== $maxWidth && 'image/jpeg' === mime_content_type($tmpFile)) { + Image::load($tmpFile)->width($maxWidth)->save(); + } + + return $model->addMedia($tmpFile); + } } diff --git a/tests/Feature/UploadTest.php b/tests/Feature/UploadTest.php index 52dfa25..211be8c 100644 --- a/tests/Feature/UploadTest.php +++ b/tests/Feature/UploadTest.php @@ -35,6 +35,25 @@ test('it uploads a single file to a single file collection', function () { $response->assertJsonMissingPath('model_id'); }); +test('it uploads a single image to a single file collection', function () { + $this->auth()->registerModel(); + $post = $this->newPost(); + $content = base64_encode($this->jpgFile()->getContent()); + + $response = $this->postJson('/mediaupload', [ + 'model' => 'post', + 'id' => $post->id, + 'collection' => 'defaultSingleFile', + 'payload' => [ + 'content' => $content, + 'name' => 'beispiel bild.jpg', + ], + ]); + + $response->assertStatus(201); + $this->assertCount(1, $post->getMedia('defaultSingleFile')); +}); + test('it forces a filename for a single collection', function () { Carbon::setTestNow(Carbon::parse('2023-04-04 00:00:00')); $this->auth()->registerModel(); diff --git a/tests/Models/Post.php b/tests/Models/Post.php index 5a3943e..2f93853 100644 --- a/tests/Models/Post.php +++ b/tests/Models/Post.php @@ -20,7 +20,7 @@ class Post extends Model implements HasMedia public function registerMediaCollections(): void { - $this->addMediaCollection('defaultSingleFile')->singleFile(); + $this->addMediaCollection('defaultSingleFile')->maxWidth(fn () => 250)->singleFile(); $this->addMediaCollection('conversionsWithDefault') ->singleFile()