'mimes:png,gif,jpg,jpeg,svg,bmp,svg+xml,webp', 'avatar' => 'mimes:png,gif,jpg,jpeg,svg,bmp,svg+xml,webp', ]; } public function response(array $errors) { return $this->redirector->back()->withInput()->withErrors($errors, $this->errorBag); } /** * Fields that should be traited from base64 to files */ protected function base64FileKeys(): array { return [ 'image' => 'auto', ]; } /** * Handle and store any images attached to request * @param SnipeModel $item Item the image is associated with * @param String $path location for uploaded images, defaults to uploads/plural of item type. * @return SnipeModel Target asset is being checked out to. */ public function handleImages($item, $w = 600, $form_fieldname = null, $path = null, $db_fieldname = 'image') { $type = strtolower(class_basename(get_class($item))); if (is_null($path)) { $path = str_plural($type); if ($type == 'assetmodel') { $path = 'models'; } if ($type == 'user') { $path = 'avatars'; } } if (is_null($form_fieldname)) { $form_fieldname = 'image'; } // This is dumb, but we need it for overriding field names for exceptions like avatars and logo uploads if (is_null($db_fieldname)) { $use_db_field = $form_fieldname; } else { $use_db_field = $db_fieldname; } \Log::info('Image path is: '.$path); \Log::debug('Type is: '.$type); \Log::debug('Form fieldname is: '.$form_fieldname); \Log::debug('DB fieldname is: '.$use_db_field); \Log::debug('Trying to upload to '. $path); \Log::debug($this->file()); if ($this->hasFile($form_fieldname)) { if (!config('app.lock_passwords')) { $image = $this->file($form_fieldname); $ext = $image->getClientOriginalExtension(); $file_name = $type.'-'.$form_fieldname.'-'.str_random(10).'.'.$ext; \Log::info('File name will be: '.$file_name); \Log::debug('File extension is: '. $ext); if (($image->getClientOriginalExtension()!=='webp') && ($image->getClientOriginalExtension()!=='svg')) { \Log::debug('Not an SVG or webp - resize'); \Log::debug('Trying to upload to: '.$path.'/'.$file_name); $upload = Image::make($image->getRealPath())->resize(null, $w, function ($constraint) { $constraint->aspectRatio(); $constraint->upsize(); }); // This requires a string instead of an object, so we use ($string) Storage::disk('public')->put($path.'/'.$file_name, (string)$upload->encode()); } else { // If the file is a webp, we need to just move it since webp support // needs to be compiled into gd for resizing to be available if ($image->getClientOriginalExtension()=='webp') { \Log::debug('This is a webp, just move it'); Storage::disk('public')->put($path.'/'.$file_name, file_get_contents($image)); // If the file is an SVG, we need to clean it and NOT encode it } else { \Log::debug('This is an SVG'); $sanitizer = new Sanitizer(); $dirtySVG = file_get_contents($image->getRealPath()); $cleanSVG = $sanitizer->sanitize($dirtySVG); try { \Log::debug('Trying to upload to: '.$path.'/'.$file_name); Storage::disk('public')->put($path.'/'.$file_name, $cleanSVG); } catch (\Exception $e) { \Log::debug('Upload no workie :( '); \Log::debug($e); } } } // Remove Current image if exists if (Storage::disk('public')->exists($path.'/'.$item->{$use_db_field})) { \Log::debug('A file already exists that we are replacing - we should delete the old one.'); try { Storage::disk('public')->delete($path.'/'.$item->{$use_db_field}); \Log::debug('Old file '.$path.'/'.$file_name.' has been deleted.'); } catch (\Exception $e) { \Log::debug('Could not delete old file. '.$path.'/'.$file_name.' does not exist?'); } } $item->{$use_db_field} = $file_name; } // If the user isn't uploading anything new but wants to delete their old image, do so } else { \Log::debug('No file passed for '.$form_fieldname); if ($this->input('image_delete')=='1') { \Log::debug('Deleting image'); try { Storage::disk('public')->delete($path . '/' . $item->{$use_db_field}); $item->{$use_db_field} = null; } catch (\Exception $e) { \Log::debug($e); } } } return $item; } /** * Helper method to get the body parameters bag. * * @return \Symfony\Component\HttpFoundation\ParameterBag */ private function bodyParametersBag(): ParameterBag { return $this->request; } /** * Helper method to get the uploaded files bag. * * @return FileBag */ private function uploadFilesBag(): FileBag { return $this->files; } /** * Pulls the Base64 contents for each file key and creates * an UploadedFile instance from it and sets it on the * request. * * @return void */ protected function prepareForValidation() { $flattened = Arr::dot($this->base64FileKeys()); Collection::make($flattened)->each(function ($filename, $key) { rescue(function () use ($key, $filename) { // dont process plain files if ( $this->file($key)){ return; } $base64Contents = $this->input($key); if (!$base64Contents) { return; } // autogenerate filenames if ($filename == 'auto'){ $header = explode(';', $base64Contents, 2)[0]; // Grab the image type from the header while we're at it. $filename = $key . '.' . substr($header, strpos($header, '/')+1); } // Generate a temporary path to store the Base64 contents $tempFilePath = tempnam(sys_get_temp_dir(), $filename); // Store the contents using a stream, or by decoding manually if (Str::startsWith($base64Contents, 'data:') && count(explode(',', $base64Contents)) > 1) { $source = fopen($base64Contents, 'r'); $destination = fopen($tempFilePath, 'w'); stream_copy_to_stream($source, $destination); fclose($source); fclose($destination); } else { file_put_contents($tempFilePath, base64_decode($base64Contents, true)); } $uploadedFile = new UploadedFile($tempFilePath, $filename, null, null, true); $body = $this->bodyParametersBag()->all(); Arr::forget($body, $key); $this->bodyParametersBag()->replace($body); $files = $this->uploadFilesBag()->all(); Arr::set($files, $key, $uploadedFile); $this->uploadFilesBag()->replace($files); }, null, false); }); } }