How to convert base64 image to UploadedFile Laravel

Viewed 9919

Vue Version : 2.6.10

Laravel Version : 6.0

I am using this vue upload package and everything is ok on client side (at least I think so). But on the server side, where I am using the laravel, have some problem.

Here is my vue send method:

        setImage: function (file) {
            let formData = new FormData();
            formData.append('file', file);
            axios.post(upload_route, formData , {
                headers: { 'Content-Type': 'multipart/form-data' }
            })
                .then(response => {
                    // upload successful
                })
                .catch(error => console.log(error));
        },

And this is my server side method:

    public function upload(Request $request){
        $path = $request->file('file')->store('avatars');
        return response('upload success' , 200);
    }

When I upload the file to the server, it gives me this error:

"message": "Call to a member function store() on null",

The file object I am sending in the setImage function is something like this (if I log it with console.log):

data:image/jpeg;base64,/9j/4AAQSkZJRgABAQAAAQABAAD/2wBDAAEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQH/2wBDAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQE...
3 Answers

I believe file parameter on setImage is not a File object. So the $request->file('file') is null, because you attach a string (base64), not a file.

You told us that output from console.log is base64 path, then you need to convert that (base64) to file.

Since you're using Laravel, here is the technique:

use Illuminate\Support\Str;
use Illuminate\Http\UploadedFile;
use Illuminate\Support\Facades\Storage;
use Symfony\Component\HttpFoundation\File\File;

.....

$base64File = $request->input('file');

// decode the base64 file
$fileData = base64_decode(preg_replace('#^data:image/\w+;base64,#i', '', $base64File));

// save it to temporary dir first.
$tmpFilePath = sys_get_temp_dir() . '/' . Str::uuid()->toString();
file_put_contents($tmpFilePath, $fileData);

// this just to help us get file info.
$tmpFile = new File($tmpFilePath);

$file = new UploadedFile(
    $tmpFile->getPathname(),
    $tmpFile->getFilename(),
    $tmpFile->getMimeType(),
    0,
    true // Mark it as test, since the file isn't from real HTTP POST.
);

$file->store('avatars');

Update

Since you're using vue-image-upload-resize, I check the documentation that it has built in function to change the output from base64 to blob, so you can just:

<image-uploader
    ...
    output-format="blob"
    ... />
<?php
if (preg_match('/^data:image\/(\w+);base64,/', $data, $type)) {
    $data = substr($data, strpos($data, ',') + 1);
    $type = strtolower($type[1]); // jpg, png, gif
​
    if (!in_array($type, [ 'jpg', 'jpeg', 'gif', 'png' ])) {
        throw new \Exception('Image Type is Not valid');
    }
    $data = base64_decode($data);
    if ($data === false) {
        throw new \Exception('Failed to Decode BASE64');
    }
} else {
    throw new \Exception('Data Not Matched With Image Data');
}
file_put_contents("image_name.{$type}", $data);//save decoded data as image 
?>

This decode and preg_match always worked for me whenever i have image like

data:image/jpeg;base64

Pass this data as $data and your extension type as $type

Found better solution, when temp files deletes after script terminate

class FileHelper
{
    public static function fromBase64(string $base64File): UploadedFile
    {
        // Get file data base64 string
        $fileData = base64_decode(Arr::last(explode(',', $base64File)));

        // Create temp file and get its absolute path
        $tempFile = tmpfile();
        $tempFilePath = stream_get_meta_data($tempFile)['uri'];

        // Save file data in file
        file_put_contents($tempFilePath, $fileData);

        $tempFileObject = new File($tempFilePath);
        $file = new UploadedFile(
            $tempFileObject->getPathname(),
            $tempFileObject->getFilename(),
            $tempFileObject->getMimeType(),
            0,
            true // Mark it as test, since the file isn't from real HTTP POST.
        );

        // Close this file after response is sent.
        // Closing the file will cause to remove it from temp director!
        app()->terminating(function () use ($tempFile) {
            fclose($tempFile);
        });

        // return UploadedFile object
        return $file;
    }
}

From https://gist.github.com/waska14/8b3bcebfad1f86f7fcd3b82927576e38

Related