first commit
This commit is contained in:
		
							
								
								
									
										28
									
								
								composer.json
									
									
									
									
									
										Executable file
									
								
							
							
						
						
									
										28
									
								
								composer.json
									
									
									
									
									
										Executable file
									
								
							| @@ -0,0 +1,28 @@ | ||||
| { | ||||
|     "name": "meoran/images", | ||||
|     "description": "Gestion des images en base de données", | ||||
|     "type": "library", | ||||
|     "license": "MIT", | ||||
|     "authors": [ | ||||
|         { | ||||
|             "name": "Cassandre Cantet", | ||||
|             "email": "pro@cassandrecantet.fr" | ||||
|         } | ||||
|     ], | ||||
|     "require": { | ||||
|         "php": ">=7.0.0", | ||||
|         "laravel/lumen-framework": "5.4.*", | ||||
|         "symfony/process" : "3.*", | ||||
|         "intervention/image": "^2.4", | ||||
|         "intervention/imagecache": "^2.3", | ||||
|         "jenssegers/model": "^1.1", | ||||
|         "spatie/laravel-image-optimizer": "^1.1" | ||||
|     }, | ||||
|     "autoload": { | ||||
|         "psr-4": { | ||||
|             "Meoran\\Images\\": "src/" | ||||
|         } | ||||
|     }, | ||||
|     "prefer-stable": true, | ||||
|     "minimum-stability": "dev" | ||||
| } | ||||
							
								
								
									
										15
									
								
								config/image.php
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										15
									
								
								config/image.php
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,15 @@ | ||||
| <?php | ||||
|  | ||||
| return [ | ||||
|     'route' => 'images', | ||||
|     'path' => storage_path('images'), | ||||
|     'templates' => array( | ||||
|         'small' => \Meoran\Images\Templates\Small::class, | ||||
|         'medium' => \Meoran\Images\Templates\Medium::class, | ||||
|         'large' => \Meoran\Images\Templates\Large::class, | ||||
|     ), | ||||
|     'lifetime' => 10, | ||||
|     'cache' => [ | ||||
|         'path' => storage_path('app') | ||||
|     ] | ||||
| ]; | ||||
| @@ -0,0 +1,34 @@ | ||||
| <?php | ||||
|  | ||||
| use Illuminate\Database\Migrations\Migration; | ||||
| use Illuminate\Database\Schema\Blueprint; | ||||
| use Illuminate\Support\Facades\Schema; | ||||
|  | ||||
| class CreateImagesTable extends Migration | ||||
| { | ||||
|     /** | ||||
|      * Run the migrations. | ||||
|      * | ||||
|      * @return void | ||||
|      */ | ||||
|     public function up() | ||||
|     { | ||||
|         Schema::create('images', function (Blueprint $table) { | ||||
|             $table->integer('id', true); | ||||
|             $table->string('filename'); | ||||
|             $table->dateTime('created_at'); | ||||
|             $table->dateTime('updated_at'); | ||||
|             $table->unique('filename'); | ||||
|         }); | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * Reverse the migrations. | ||||
|      * | ||||
|      * @return void | ||||
|      */ | ||||
|     public function down() | ||||
|     { | ||||
|         Schema::drop('images'); | ||||
|     } | ||||
| } | ||||
| @@ -0,0 +1,38 @@ | ||||
| <?php | ||||
|  | ||||
| use Illuminate\Database\Migrations\Migration; | ||||
| use Illuminate\Database\Schema\Blueprint; | ||||
| use Illuminate\Support\Facades\Schema; | ||||
|  | ||||
| class CreateAssociateImages extends Migration | ||||
| { | ||||
|     /** | ||||
|      * Run the migrations. | ||||
|      * | ||||
|      * @return void | ||||
|      */ | ||||
|     public function up() | ||||
|     { | ||||
|         Schema::create('associate_images', function (Blueprint $table) { | ||||
|             $table->integer('id', true); | ||||
|             $table->integer('image_id'); | ||||
|             $table->string('relation_type'); | ||||
|             $table->integer('relation_id'); | ||||
|             $table->integer('position')->nullable(); | ||||
|  | ||||
|             $table->foreign('image_id')->references('id')->on('images'); | ||||
|  | ||||
|         }); | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * Reverse the migrations. | ||||
|      * | ||||
|      * @return void | ||||
|      */ | ||||
|     public function down() | ||||
|     { | ||||
|         Schema::drop('associate_images'); | ||||
|  | ||||
|     } | ||||
| } | ||||
							
								
								
									
										129
									
								
								src/Http/Controllers/ImagesController.php
									
									
									
									
									
										Executable file
									
								
							
							
						
						
									
										129
									
								
								src/Http/Controllers/ImagesController.php
									
									
									
									
									
										Executable file
									
								
							| @@ -0,0 +1,129 @@ | ||||
| <?php | ||||
|  | ||||
| namespace Meoran\Images\Http\Controllers; | ||||
|  | ||||
| use Closure; | ||||
| use Laravel\Lumen\Routing\Controller as BaseController; | ||||
| use Meoran\Images\Model\Image; | ||||
|  | ||||
| class ImagesController extends BaseController | ||||
| { | ||||
|  | ||||
|     public function get($template, $filename) | ||||
|     { | ||||
|         switch (strtolower($template)) { | ||||
|             case 'original': | ||||
|                 return $this->getOriginal($filename); | ||||
|             case 'download': | ||||
|                 return $this->getDownload($filename); | ||||
|             default: | ||||
|                 return $this->getImage($template, $filename); | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     public function upload() | ||||
|     { | ||||
|         $pathImage = storage_path('Sunset_at_Tiergarten_UHD.jpg'); | ||||
|  | ||||
|         $image = new Image(['content' => $pathImage]); | ||||
|         $image->save(); | ||||
|  | ||||
|         return response()->json($image); | ||||
|     } | ||||
|  | ||||
|     private function getOriginal($filename) | ||||
|     { | ||||
|         $path = $this->getImagePathOrAbort($filename); | ||||
|         return $this->buildResponse(file_get_contents($path)); | ||||
|     } | ||||
|  | ||||
|     private function getDownload($filename) | ||||
|     { | ||||
|         $response = $this->getOriginal($filename); | ||||
|  | ||||
|         return $response->header( | ||||
|             'Content-Disposition', | ||||
|             'attachment; filename=' . $filename | ||||
|         ); | ||||
|     } | ||||
|  | ||||
|     private function getImagePathOrAbort($filename) | ||||
|     { | ||||
|         $path = Image::getAbsolutePath($filename); | ||||
|         if (is_file($path)) { | ||||
|             return $path; | ||||
|         } | ||||
|         abort(404); | ||||
|     } | ||||
|  | ||||
|     public function getImage($template, $filename) | ||||
|     { | ||||
|         $template = $this->getTemplate($template); | ||||
|         $path = $this->getImagePathOrAbort($filename); | ||||
|  | ||||
|         $lifetime = config('image.lifetime'); | ||||
|         if (empty($lifetime)) { | ||||
|             $content = $this->applyTemplate($template,$path)->encode(); | ||||
|         } else { | ||||
|             $content = app('image')->cache(function ($image) use ($template, $path) { | ||||
|                 $this->applyTemplate($template,$path,$image); | ||||
|                 return $image; | ||||
|             }, $lifetime); | ||||
|         } | ||||
|  | ||||
|         return $this->buildResponse($content); | ||||
|     } | ||||
|  | ||||
|     private function applyTemplate($template, $path, $image = null) | ||||
|     { | ||||
|         if (empty($image)) { | ||||
|             $image = app('image'); | ||||
|         } | ||||
|  | ||||
|         if ($template instanceof Closure) { | ||||
|             // build from closure callback template | ||||
|             return $template($image->make($path)); | ||||
|         } else { | ||||
|             // build from filter template | ||||
|             return $image->make($path)->filter($template); | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * Returns corresponding template object from given template name | ||||
|      * | ||||
|      * @param  string $template | ||||
|      * @return mixed | ||||
|      */ | ||||
|     private function getTemplate($template) | ||||
|     { | ||||
|         $template = config("image.templates.{$template}"); | ||||
|         switch (true) { | ||||
|             // closure template found | ||||
|             case is_callable($template): | ||||
|                 return $template; | ||||
|  | ||||
|             // filter template found | ||||
|             case class_exists($template): | ||||
|                 return new $template; | ||||
|  | ||||
|             default: | ||||
|                 // template not found | ||||
|                 abort(404); | ||||
|                 break; | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     private function buildResponse($content) | ||||
|     { | ||||
|         // define mime type | ||||
|         $mime = finfo_buffer(finfo_open(FILEINFO_MIME_TYPE), $content); | ||||
|  | ||||
|         // return http response | ||||
|         return response($content, 200, array( | ||||
|             'Content-Type' => $mime, | ||||
|             'Cache-Control' => 'max-age=' . (config('image.lifetime') * 60) . ', public', | ||||
|             'Etag' => md5($content) | ||||
|         )); | ||||
|     } | ||||
| } | ||||
							
								
								
									
										140
									
								
								src/Model/Image.php
									
									
									
									
									
										Executable file
									
								
							
							
						
						
									
										140
									
								
								src/Model/Image.php
									
									
									
									
									
										Executable file
									
								
							| @@ -0,0 +1,140 @@ | ||||
| <?php | ||||
|  | ||||
| namespace Meoran\Images\Model; | ||||
|  | ||||
| use Illuminate\Database\Eloquent\Model; | ||||
| use Spatie\ImageOptimizer\OptimizerChain; | ||||
|  | ||||
| /** | ||||
|  * Class Image | ||||
|  * @property Image $content | ||||
|  * @package App\Model | ||||
|  */ | ||||
| class Image extends Model | ||||
| { | ||||
|  | ||||
|     protected $_content; | ||||
|  | ||||
|     public $fillable = [ | ||||
|         'content', | ||||
|         'position', | ||||
|         'created', | ||||
|         'updated' | ||||
|     ]; | ||||
|     protected $dates = ['created_at', 'updated_at']; | ||||
|     protected $appends = ['url']; | ||||
|     protected $hidden = []; | ||||
|  | ||||
|     public function getUrlAttribute() | ||||
|     { | ||||
|         return app('url')->to('/'.config('image.route').'/original/' . $this->filename); | ||||
|     } | ||||
|  | ||||
|     protected static function boot() | ||||
|     { | ||||
|         parent::boot(); | ||||
|  | ||||
|         static::creating(function(Image $model) { | ||||
|             if (empty($model->content)) { | ||||
|                 throw new \Exception("Content of picture can't be empty at creation"); | ||||
|             } | ||||
|             $model->savePicture(); | ||||
|         }); | ||||
|  | ||||
|         static::updating(function (Image $model) { | ||||
|             $model->savePicture(); | ||||
|         }); | ||||
|  | ||||
|         static::deleted(function (Image $model) { | ||||
|             $model->deletePicture(); | ||||
|         }); | ||||
|  | ||||
|     } | ||||
|  | ||||
|     public function getPath() | ||||
|     { | ||||
|         if (empty($this->filename)) { | ||||
|             return null; | ||||
|         } | ||||
|  | ||||
|         return self::getAbsolutePath($this->filename); | ||||
|     } | ||||
|  | ||||
|     static function getAbsolutePath($filename) | ||||
|     { | ||||
|         $basePath = config('image.path'); | ||||
|  | ||||
|         $parts = array_slice(str_split($filename, 2), 0, 2); | ||||
|  | ||||
|         $path = $basePath.'/'.implode('/', $parts).'/'.$filename; | ||||
|  | ||||
|         return $path; | ||||
|     } | ||||
|  | ||||
|     static function generateRandomFilename() | ||||
|     { | ||||
|         return mb_strtolower(str_random(60)); | ||||
|     } | ||||
|  | ||||
|     public function generateFilename($force = false) | ||||
|     { | ||||
|         if ($this->filename && !$force) { | ||||
|             return; | ||||
|         } | ||||
|         $this->filename = self::generateRandomFilename(); | ||||
|     } | ||||
|  | ||||
|     public function setContentAttribute($content) | ||||
|     { | ||||
|         $this->_content = app('image')->make($content); | ||||
|     } | ||||
|  | ||||
|     public function getContentAttribute($value) | ||||
|     { | ||||
|         return $this->_content; | ||||
|     } | ||||
|  | ||||
|     protected function savePicture() | ||||
|     { | ||||
|         if (empty($this->content)) { | ||||
|             return true; | ||||
|         } | ||||
|         $this->generateFilename(); | ||||
|         return $this->saveContent(); | ||||
|     } | ||||
|  | ||||
|     protected function saveContent() | ||||
|     { | ||||
|         if (empty($this->content)) { | ||||
|             throw new \InvalidArgumentException("Content is Empty"); | ||||
|         } | ||||
|         $path = $this->getPath(); | ||||
|         $dir = dirname($path); | ||||
|         if (!is_dir($dir)) { | ||||
|             mkdir($dir, 0775, true); | ||||
|         } | ||||
|         $res = $this->content->save($path); | ||||
|         app(OptimizerChain::class)->optimize($path); | ||||
|         return $res; | ||||
|     } | ||||
|  | ||||
|     protected function deletePicture() | ||||
|     { | ||||
|         $path = $this->getPath(); | ||||
|         if (is_file($path)) { | ||||
|             return unlink($path); | ||||
|         } | ||||
|         return true; | ||||
|     } | ||||
|  | ||||
|     public function toArray() | ||||
|     { | ||||
|         $attributes = parent::toArray(); | ||||
|  | ||||
|         if (isset($attributes['pivot']) && array_key_exists('position', $attributes['pivot'])) { | ||||
|             $attributes['position'] = $attributes['pivot']['position']; | ||||
|         } | ||||
|         unset($attributes['pivot']); | ||||
|         return $attributes; | ||||
|     } | ||||
| } | ||||
							
								
								
									
										44
									
								
								src/Providers/ImagesServiceProvider.php
									
									
									
									
									
										Executable file
									
								
							
							
						
						
									
										44
									
								
								src/Providers/ImagesServiceProvider.php
									
									
									
									
									
										Executable file
									
								
							| @@ -0,0 +1,44 @@ | ||||
| <?php | ||||
|  | ||||
| namespace Meoran\Images\Providers; | ||||
|  | ||||
| use Illuminate\Support\ServiceProvider; | ||||
| use Intervention\Image\ImageServiceProvider; | ||||
| use Spatie\LaravelImageOptimizer\ImageOptimizerServiceProvider; | ||||
|  | ||||
| class ImagesServiceProvider extends ServiceProvider | ||||
| { | ||||
|  | ||||
|     public function boot() | ||||
|     { | ||||
|         require_once __DIR__ . '/../functions.php'; | ||||
|  | ||||
|         $this->loadMigrationsFrom(__DIR__ . '/../../database/migrations'); | ||||
|  | ||||
|         $this->mergeConfigFrom(__DIR__ . '/../../config/image.php', 'image'); | ||||
|  | ||||
|  | ||||
|         $this->app->register(ImageServiceProvider::class); | ||||
|         $this->app->register(ImageOptimizerServiceProvider::class); | ||||
|  | ||||
|         $this->loadRoutes(); | ||||
|  | ||||
|         if ($this->app->runningInConsole()) { | ||||
|             $this->publishes([ | ||||
| //                __DIR__ . '/../../config/synchronize.php' => base_path('config/synchronize.php') | ||||
|             ]); | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     public function loadRoutes() | ||||
|     { | ||||
|         $this->app->get('images/upload', [ | ||||
|             'as' => 'uploadImage', 'uses' => '\Meoran\Images\Http\Controllers\ImagesController@upload' | ||||
|         ]); | ||||
|  | ||||
|         $this->app->get('images/{template}/{filename}', [ | ||||
|             'as' => 'getPicture', 'uses' => '\Meoran\Images\Http\Controllers\ImagesController@get' | ||||
|         ]); | ||||
|     } | ||||
|  | ||||
| } | ||||
							
								
								
									
										18
									
								
								src/Templates/Large.php
									
									
									
									
									
										Executable file
									
								
							
							
						
						
									
										18
									
								
								src/Templates/Large.php
									
									
									
									
									
										Executable file
									
								
							| @@ -0,0 +1,18 @@ | ||||
| <?php | ||||
|  | ||||
| namespace Meoran\Images\Templates; | ||||
|  | ||||
| use Intervention\Image\Constraint; | ||||
| use Intervention\Image\Filters\FilterInterface; | ||||
| use Intervention\Image\Image; | ||||
|  | ||||
| class Large implements FilterInterface | ||||
| { | ||||
|     public function applyFilter(Image $image) | ||||
|     { | ||||
|         return $image->resize(1920, null, function (Constraint $constraint) { | ||||
|             $constraint->upsize(); | ||||
|             $constraint->aspectRatio(); | ||||
|         }); | ||||
|     } | ||||
| } | ||||
							
								
								
									
										18
									
								
								src/Templates/Medium.php
									
									
									
									
									
										Executable file
									
								
							
							
						
						
									
										18
									
								
								src/Templates/Medium.php
									
									
									
									
									
										Executable file
									
								
							| @@ -0,0 +1,18 @@ | ||||
| <?php | ||||
|  | ||||
| namespace Meoran\Images\Templates; | ||||
|  | ||||
| use Intervention\Image\Constraint; | ||||
| use Intervention\Image\Filters\FilterInterface; | ||||
| use Intervention\Image\Image; | ||||
|  | ||||
| class Medium implements FilterInterface | ||||
| { | ||||
|     public function applyFilter(Image $image) | ||||
|     { | ||||
|         return $image->resize(240, null, function (Constraint $constraint) { | ||||
|             $constraint->upsize(); | ||||
|             $constraint->aspectRatio(); | ||||
|         }); | ||||
|     } | ||||
| } | ||||
							
								
								
									
										18
									
								
								src/Templates/Small.php
									
									
									
									
									
										Executable file
									
								
							
							
						
						
									
										18
									
								
								src/Templates/Small.php
									
									
									
									
									
										Executable file
									
								
							| @@ -0,0 +1,18 @@ | ||||
| <?php | ||||
|  | ||||
| namespace Meoran\Images\Templates; | ||||
|  | ||||
| use Intervention\Image\Constraint; | ||||
| use Intervention\Image\Filters\FilterInterface; | ||||
| use Intervention\Image\Image; | ||||
|  | ||||
| class Small implements FilterInterface | ||||
| { | ||||
|     public function applyFilter(Image $image) | ||||
|     { | ||||
|         return $image->resize(120, null, function (Constraint $constraint) { | ||||
|             $constraint->upsize(); | ||||
|             $constraint->aspectRatio(); | ||||
|         }); | ||||
|     } | ||||
| } | ||||
							
								
								
									
										14
									
								
								src/functions.php
									
									
									
									
									
										Executable file
									
								
							
							
						
						
									
										14
									
								
								src/functions.php
									
									
									
									
									
										Executable file
									
								
							| @@ -0,0 +1,14 @@ | ||||
| <?php | ||||
|  | ||||
| if (!function_exists('config_path')) { | ||||
|     /** | ||||
|      * Get the configuration path. | ||||
|      * | ||||
|      * @param  string $path | ||||
|      * @return string | ||||
|      */ | ||||
|     function config_path($path = '') | ||||
|     { | ||||
|         return app()->basePath() . '/config' . ($path ? '/' . $path : $path); | ||||
|     } | ||||
| } | ||||
		Reference in New Issue
	
	Block a user