timgavin's avatar

PDF Passes Image Validation

I'm uploading multiple files through Livewire and am validating in the updated() method for real-time validation. For some reason, when I select a PDF file it passes validation and I get the "This driver does not support creating temporary URLs" validation error, which tells me the PDF is passing validation.

I've tried 2 different methods of validation and both are allowing the PDF to pass; am I doing something wrong?

$public $photos = [];

public function updated()
{
    $this->validate([
        'photos.*' => 'mimes:jpg,jpeg,gif,png',
    ]);
}
$public $photos = [];

public function updated()
{
    $validator = Validator::make(
        ['photos.*' => $this->photos],
        ['photos.*' => 'mimes:jpg,jpeg,gif,png'],
    );

    if ($validator->fails()) {
        $this->reset('photos');
        $this->setErrorBag($validator->getMessageBag());
    }
}
0 likes
12 replies
Snapey's avatar

are you actually hitting this validator?

timgavin's avatar

@Snapey Yes. If I dd() on the first line it returns.

In fact, doing the following with a font file also resulted in "passed validation" being dumped to the screen

$validator = Validator::make(
    ['photos.*' => $this->photos],
    ['photos.*' => 'mimes:jpg,jpeg,gif,png'],
);

dd('passed validation');
timgavin's avatar

@Snapey I think so. And it's working fine with uploading multiple images; each image preview shows in the view as it should. Basically everything is working as expected except the validation.

I created a new Laravel app and tested the same validation just to make sure it wasn't a bug with Laravel, and it worked as expected; PDFs did not pass validation.

My input

<input wire:model="photos" type="file" id="file" multiple>

The Livewire model

class MyPhotos extends Component
{
    use WithFileUploads;

    public $photos = [];

    public function updated()
    {
        $validator = Validator::make(
            ['photos.*' => $this->photos],
            ['photos.*' => 'mimes:jpg,jpeg,gif,png'],
        );

        dd('passed validation');

        if ($validator->fails()) {
            $this->reset('photos');
            $this->setErrorBag($validator->getMessageBag());
        }
    }
...
Snapey's avatar

@timgavin try

        $validator = Validator::make(
            ['photos' => $this->photos],
            ['photos.*' => 'mimes:jpg,jpeg,gif,png'],
        );
Snapey's avatar

@timgavin how about simply

        $this->validate([
			'photos.*' => 'mimes:jpg,jpeg,gif,png'
        ]);
Brammah's avatar

@timgavin shouldn't the photo be of type image?

    ['photos.*' => $this->photos],
    ['photos.*' => 'image|mimes:jpg,jpeg,gif,png'],
);
timgavin's avatar

@Brammah That was the first thing I tried. It appears that something is broken with Livewire's validation, as a PDF (or even a font file) will pass validation and throw the exception.

I created a new Laravel app and tested my validation rules without Livewire and they worked correctly; this is a Livewire bug.

Edit. To be clear, this bug appears when the files are selected in the browser; e.g.; real-time validation. Validation rules still work properly upon save() and an error message "photos must be an image" is shown, as it should.

timgavin's avatar

Looks like Livewire may actually be designed this way. Inside the temporaryUrl() method we find the following

if (method_exists($this->storage->getAdapter(), 'getTemporaryUrl') || ! $this->isPreviewable()) {
    // This will throw an error because it's not used with S3.
    return $this->storage->temporaryUrl($this->path, now()->addDay());
}

And since I'm testing locally, it is indeed throwing an error. Hmmmm....

timgavin's avatar

Looks like this is a long standing bug with Livewire that's probably not going to be fixed any time soon. I've adopted a workaround that actually fixes this issue.

I added a block of code that shows a missing image icon for files that cannot show a preview. I HATE having to edit a library file, but Livewire is unusable for image uploads out of the box because of this bug, so...

This is a bad solution though (editing the source files), and I don't really want to adopt it, so I'm going to continue to look for a better way...

// vendor/livewire/livewire/src/TemporaryUploadedFile.php

public function temporaryUrl()
{
    if (! $this->isPreviewable()) {
        // show a missing image icon (?) for files that cannot be previewed
        return 'data:image/png;base64...gg==';
    }
    ...
}

Please or to participate in this conversation.