So I actually just ended up modifying my functions to accept a binary string and then just duplicating some logic. Oh well.
Can I turn a base64 string into an UploadedFile object?
Basically what I'm wanting to do here is accept a base64 encoded image from the client, convert that to an UploadedFile (like is made when you upload a file like normal), and pass that to the function that I normally use to process file uploads. This will later be uploaded to Amazon S3, preferably without writing the image to my server at any point. Can this be done?
You broke the rule - 'dont repeat yourself'. Other solutions here? I need it too :)
Could you do something like this:
use Illuminate\Http\File;
// Automatically generate a unique ID for file name...
Storage::disk('s3')->putFile('photos', base64_decode(request('file'));
I didn't try running this code, but it seems like it should be able to be done like that or something similar.
I just finished working out the exact same scenario.
-
Put the following in your
AppServiceProvider::boot()method:File::macro('streamMimeType', function ($content) { $mimeType = finfo_buffer(finfo_open(), $content, FILEINFO_MIME_TYPE); return $mimeType; }); File::macro('streamSize', function ($content) { $size = strlen($content); return $size; }); File::macro('mimeExtension', function ($mimeType) { $extension = ExtensionGuesser::getInstance()->guess($mimeType); return $extension; }); File::macro('streamExtension', function ($content) { $mimeType = $this->streamMimeType($content); $extension = $this->streamMimeExtension($mimeType); return $extension; }); -
Create a custom Request object that extends
Illuminate\Http\Request, and make sure theprotected $rulesarray includes a['your_file_param' => 'file']element. https://laravel.com/docs/5.6/validation#creating-form-requests https://laravel.com/docs/5.6/validation#rule-file -
Leverage the optional
prepareForValidation()method in your new Request object, and place the following inside it:$rules = $this->rules(); foreach ($rules as $field => $ruleSet) { $ruleSet = is_string($ruleSet) ? explode('|', $ruleSet) : $ruleSet; if (in_array('file', $ruleSet) && $this->has($field)) { if (!$this->file($field) && ($content = $this->input($field)) && is_string($content)) { //"data:image/png;base64,iVBORw0K..."; $content = array_last(explode(',', $content)); $content = base64_decode($content); $mimeType = File::streamMimeType($content); $extension = File::mimeExtension($mimeType); $size = File::streamSize($content); $name = uniqid('decoded_').'.'.$extension; $path = tempnam(sys_get_temp_dir(), uniqid('symfony', true)); File::put($path, $content); $file = app(UploadedFile::class, [ 'path' => $path, 'originalName' => $name, 'mimeType' => $mimeType, 'size' => $size, 'error' => null, 'test' => true ]); $this->files->add([$field => $file]); $this->offsetUnset($field); } } }You can name it whatever you like of course, not just uniqid('decoded_'). Also, that
$pathwas derived the same exact waySymfony\Bridge\PsrHttpMessage\Factory\HttpFoundationFactory::getTemporaryPath()does it when constructing the defaultRequestfiles in the first place. Sadly,getTemporaryPath()is protected, so there's no accessing that behavior without making a ServiceProvider bind an override class in place of HttpFoundationFactory, and that's more trouble than it's worth. -
Type-hint your new custom Request object into your controller method, and use that instead of the
request()helper function. It should then have access to your UploadFile incarnation of the base64 input.
Please or to participate in this conversation.