Skip to content

Instantly share code, notes, and snippets.

@enniosousa
Last active August 23, 2021 19:18
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save enniosousa/7b0b54fa7be4709d7c5d96c5c4be625d to your computer and use it in GitHub Desktop.
Save enniosousa/7b0b54fa7be4709d7c5d96c5c4be625d to your computer and use it in GitHub Desktop.

Base64 data URI as binary upload

Laravel does not support base64 validation but have a lot of native validation to binary file like size, mime types, dimentions...

So the best way that I found is cast base64 to binary

How to use

Create a trait in your project and use in your request appending the following method:

    /**
     * Prepare the data for validation.
     *
     * @return void
     */
    protected function withValidator($validator)
    {
        $this->castBase64ToUploadedFile($validator, $attrs = ['file']);
    }
<?php
namespace App\Support\Request;
use Illuminate\Http\Testing\MimeType;
use Illuminate\Support\Str;
use Illuminate\Support\Arr;
use Illuminate\Validation\Validator ;
use Symfony\Component\HttpFoundation\File\UploadedFile;
trait Base64AsBinary
{
/**
* If the attributes values is base64 data URI the cast to binary files
* Then you can handle in laravel as a binare uploaded file
*
* Example in request:
* ```
* protected function withValidator($validator)
* {
* $this->castBase64ToUploadedFile($validator, $attrs = ['logo']);
* }
* ```
*
* @param Validator $validator
* @param string|array $attributes - attribute name (as string, or array of strings)
* @return void
*/
public function castBase64ToUploadedFile(Validator $validator, $attributes)
{
$data = $validator->getData();
foreach (Arr::wrap($attributes) as $attr) {
$value = Arr::get($data, $attr);
if (!$value) {
continue;
}
if ($value instanceof UploadedFile) {
// no nothing
}
// cast base64 to UploadedFile
elseif (
is_string($value)
&& $this->isBase64($value)
) {
$file = $this->mediaFromBase64($value);
$data[$attr] = $file;
$this->request->remove($attr);
$this->files->set($attr, $file);
}
}
// refresh convertedFiles Cache
if (array_keys($this->files->all()) !== array_keys($this->allFiles())) {
$this->convertedFiles = $this->convertUploadedFiles(
$this->files->all()
);
}
$validator->setData($data);
}
/**
* Validate base64 data uri according to RFC 2397 with regex pattern
*
* @param string $base64
* @return boolean
*/
private function isBase64(string $base64): bool
{
/**
* @link https://gist.github.com/khanzadimahdi/bab8a3416bdb764b9eda5b38b35735b8
*/
$re = '/^data:((?:\w+\/(?:(?!;).)+)?)((?:;[\w\W]*?[^;])*),(.+)$/';
return Str::matchAll($re, $base64)->isNotEmpty();
}
/**
* Create `UploadedFile` instance From base64 data URI
*
* @param string $base64data
* @return UploadedFile
*/
private function mediaFromBase64(string $base64data): UploadedFile
{
// strip out data uri scheme information (see RFC 2397)
if (strpos($base64data, ';base64') !== false) {
[$dataMime, $base64data] = explode(';', $base64data);
[$_, $base64data] = explode(',', $base64data);
[$_, $mimeType] = explode(':', $dataMime);
}
// strict mode filters for non-base64 alphabet characters
$binaryData = base64_decode($base64data, true);
// temporarily store the decoded data on the filesystem to be able to pass it to the fileAdder
$tmpFile = tempnam(sys_get_temp_dir(), 'file');
file_put_contents($tmpFile, $binaryData);
// filename with extension
$filename = 'file.' . MimeType::search($mimeType);
$file = new UploadedFile(
$tmpFile,
$filename,
$mimeType,
/**
* IMPORTANT TO KNOW
* The following paramters avoid `is_uploaded_file` validation.
* Be sure that is validation mime types and other validation rules to be safe
* @link https://www.php.net/manual/pt_BR/function.is-uploaded-file.php
*/
$error = UPLOAD_ERR_OK,
$test = true
);
return $file;
}
}
<?php
namespace App\Http\Requests\Medias;
use Illuminate\Foundation\Http\FormRequest;
use Illuminate\Support\Facades\Auth;
use App\Support\Request\Base64AsBinary; // <<------------------ IMPORT THE TRAIT NAMESPACE
class MediasRequest extends FormRequest
{
use Base64AsBinary; // <<------------------ USE THE TRAIT IN REQUEST CLASS
/**
* Determine if the user is authorized to make this request.
*
* @return bool
*/
public function authorize()
{
return Auth::check();
}
/**
* Get the validation rules that apply to the request.
*
* @return array
*/
public function rules()
{
return [
'file' => [
'required',
'image',
'mimes:jpg,png'
],
];
}
/**
* Prepare the data for validation.
*
* @return void
*/
protected function withValidator($validator)
{
$this->castBase64ToUploadedFile($validator, $attrs = ['file']);
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment