jsphpndr's avatar

Intervention (v3) storing image in binary

So, in my local environment, the below works:

            $image_path = $request->hasFile('image') ? $client->image : '/defaults/default-avatar.jpg';


            if ($request->hasFile('image')) {

                // Get the old image path
                $old_image_path = $client->image;

                // Remove the '/storage/' prefix
                $old_image_path = Str::replaceFirst('/storage/', '', $old_image_path);

                // Delete the old image if it exists
                if ($old_image_path && Storage::disk('public')->exists($old_image_path)) {
                    Storage::disk('public')->delete($old_image_path);
                }

                $image = $request->file('image');

                $manager = new ImageManager(new Driver());

                $uuid = Str::uuid()->toString(); // Generate a UUID as a string
                $name_gen = "{$client->slug}-{$uuid}.jpg";
                $image = $manager->read($image);
                $image = $image->coverDown(1200, 1200, 'center');

                $image->toJpeg(72)->save(base_path("storage/app/public/uploads/{$name_gen}"));

                $image_path = "/storage/uploads/{$name_gen}";
                
                $client->update(['image' => $image_path]);
                
            }

            unset($data['image']);

The image is uploaded and saved correctly.

On the production server, the first issue I had was that Laravel/Intervention did not create the storage folder, so I was getting a '500 server error'.

After manually creating the uploads folder, I was able to upload and save, but now I'm getting a broken image. I check the file manager and the file exists, but i get this error:

Error
File Manager Error: Unable to open /home/runcloud/webapps/creditworthy/storage/app/public/uploads/john-doe-1-7ea08852-cc14-4f2f-bfd1-f96cb1e8a6c6.jpg file. It is a binary file!

Shouldn't this, $image->toJpeg(72)->save(base_path("storage/app/public/uploads/{$name_gen}"));, account for the JPG format in which it's supposed to be saved? The filename is correct... but, yeah...

0 likes
1 reply
martinbean's avatar

@jsphpndr Why are you manually generating “random” filenames? That’s literally what Laravel does if you use its file uploading helpers proplerly. See: https://laravel.com/docs/11.x/requests#storing-uploaded-files

All you need to do is upload the new file, and set up some sort of listener that deletes the old file from storage if the column is updated on your model:

if ($image = $request->file('image')) {
    $client->update([
        'image' => $image->store('uploads'),
    ]);
}

And then to clean up old images:

class ClientObserver
{
    public function saved(Client $client)
    {
        if ($client->isDirty('image')) {
            Storage::delete($client->getOriginal('image'));
        }
    }
}

You also should be using Laravel’s disk properly, instead of all that manual string-replacing paths and folders names and whatnot.

Also, you should not be storing user-uploaded files in a publicly-accessible location, because if a user manages to upload a malicious file they can now access and use that file to hack your web application and the server that it is running on. You should instead be uploading files to storage, and then serving generated thumbnails of those images. You can either pre-generate the thumbnails if you know the dimensions you need and store those in a publicly-accessible directory, or you should use something like Glide to dynamically generate and serve thumbnails.

Please or to participate in this conversation.