From e8f022ca7e5b0e687838405d89b39065004f2a03 Mon Sep 17 00:00:00 2001 From: Coassolo-Lucas Date: Wed, 27 May 2026 21:22:57 -0300 Subject: [PATCH] Imagenes en productos. --- app/Http/Controllers/ProductosController.php | 60 +++++++- composer.json | 1 + composer.lock | 146 ++++++++++++++++++- public/.htaccess | 9 ++ public/.user.ini | 4 + 5 files changed, 215 insertions(+), 5 deletions(-) create mode 100644 public/.user.ini diff --git a/app/Http/Controllers/ProductosController.php b/app/Http/Controllers/ProductosController.php index ea104a4..3e8d234 100644 --- a/app/Http/Controllers/ProductosController.php +++ b/app/Http/Controllers/ProductosController.php @@ -68,13 +68,39 @@ class ProductosController extends Controller 'min_stock_alert' => 'required|integer|min:0', 'type' => 'required|in:bike,accessory,clothing,spare,service,children,skate,rollers,other', 'serial_number' => 'nullable|string|max:100', - 'image' => 'nullable|image|mimes:jpeg,png,jpg,webp|max:2048', + 'image' => 'nullable|image|mimes:jpeg,png,jpg,webp|max:20480', 'suppliers_id' => 'required|exists:suppliers,id', 'description' => 'nullable|string' + ], [ + 'image.uploaded' => 'La imagen supera el límite de subida permitido por el servidor.', + 'image.max' => 'La imagen no debe pesar más de 20 MB.', + 'image.image' => 'El archivo debe ser una imagen válida.', + 'image.mimes' => 'La imagen debe tener formato jpeg, png, jpg o webp.', ]); if ($request->hasFile('image')) { - $path = $request->file('image')->store('products', 'public'); + $file = $request->file('image'); + + // Elevar temporalmente el límite de memoria para procesar la imagen + ini_set('memory_limit', '512M'); + + // Usar Intervention Image v4 con driver GD para procesar la imagen + $manager = new \Intervention\Image\ImageManager(new \Intervention\Image\Drivers\Gd\Driver()); + $image = $manager->decode($file->getRealPath()); + + // Redimensionar proporcionalmente si excede 1200px + if ($image->width() > 1200 || $image->height() > 1200) { + $image->scale(width: 1200, height: 1200); + } + + // Comprimir a JPEG con 80% de calidad + $encoded = $image->encode(new \Intervention\Image\Encoders\JpegEncoder(80)); + + // Nombre de archivo único + $path = 'products/' . uniqid() . '.jpg'; + + // Guardar en disco public + Storage::disk('public')->put($path, $encoded->toString()); $validated['image_path'] = $path; } unset($validated['image']); @@ -106,9 +132,14 @@ class ProductosController extends Controller 'stock_quantity' => 'required|integer|min:0', 'min_stock_alert' => 'required|integer|min:0', 'serial_number' => 'nullable|string|max:100', - 'image' => 'nullable|image|mimes:jpeg,png,jpg,webp|max:2048', + 'image' => 'nullable|image|mimes:jpeg,png,jpg,webp|max:20480', 'suppliers_id' => 'required|exists:suppliers,id', 'description' => 'nullable|string' + ], [ + 'image.uploaded' => 'La imagen supera el límite de subida permitido por el servidor.', + 'image.max' => 'La imagen no debe pesar más de 20 MB.', + 'image.image' => 'El archivo debe ser una imagen válida.', + 'image.mimes' => 'La imagen debe tener formato jpeg, png, jpg o webp.', ]); if ($request->hasFile('image')) { @@ -116,7 +147,28 @@ class ProductosController extends Controller Storage::disk('public')->delete($product->image_path); } - $path = $request->file('image')->store('products', 'public'); + $file = $request->file('image'); + + // Elevar temporalmente el límite de memoria para procesar la imagen + ini_set('memory_limit', '512M'); + + // Usar Intervention Image v4 con driver GD para procesar la imagen + $manager = new \Intervention\Image\ImageManager(new \Intervention\Image\Drivers\Gd\Driver()); + $image = $manager->decode($file->getRealPath()); + + // Redimensionar proporcionalmente si excede 1200px + if ($image->width() > 1200 || $image->height() > 1200) { + $image->scale(width: 1200, height: 1200); + } + + // Comprimir a JPEG con 80% de calidad + $encoded = $image->encode(new \Intervention\Image\Encoders\JpegEncoder(80)); + + // Nombre de archivo único + $path = 'products/' . uniqid() . '.jpg'; + + // Guardar en disco public + Storage::disk('public')->put($path, $encoded->toString()); $validated['image_path'] = $path; } unset($validated['image']); diff --git a/composer.json b/composer.json index 2a90057..60fd95c 100644 --- a/composer.json +++ b/composer.json @@ -7,6 +7,7 @@ "license": "MIT", "require": { "php": "^8.2", + "intervention/image": "^4.1", "laravel-lang/common": "^6.7", "laravel/framework": "^12.0", "laravel/tinker": "^2.10.1" diff --git a/composer.lock b/composer.lock index 566afb2..2764864 100644 --- a/composer.lock +++ b/composer.lock @@ -4,7 +4,7 @@ "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies", "This file is @generated automatically" ], - "content-hash": "c5327d228d42dad14f6185dd57e05dde", + "content-hash": "54a99d812eb7e5412ada7f4366c3065c", "packages": [ { "name": "archtechx/enums", @@ -1568,6 +1568,150 @@ ], "time": "2025-02-03T10:55:03+00:00" }, + { + "name": "intervention/gif", + "version": "5.0.1", + "source": { + "type": "git", + "url": "https://github.com/Intervention/gif.git", + "reference": "bb395af960deffe64d70c976b4df9283f68e762d" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/Intervention/gif/zipball/bb395af960deffe64d70c976b4df9283f68e762d", + "reference": "bb395af960deffe64d70c976b4df9283f68e762d", + "shasum": "" + }, + "require": { + "php": "^8.3" + }, + "require-dev": { + "phpstan/phpstan": "^2.1", + "phpunit/phpunit": "^12.0", + "slevomat/coding-standard": "~8.0", + "squizlabs/php_codesniffer": "^4" + }, + "type": "library", + "autoload": { + "psr-4": { + "Intervention\\Gif\\": "src" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Oliver Vogel", + "email": "oliver@intervention.io", + "homepage": "https://intervention.io/" + } + ], + "description": "PHP GIF Encoder/Decoder", + "homepage": "https://github.com/intervention/gif", + "keywords": [ + "animation", + "gd", + "gif", + "image" + ], + "support": { + "issues": "https://github.com/Intervention/gif/issues", + "source": "https://github.com/Intervention/gif/tree/5.0.1" + }, + "funding": [ + { + "url": "https://paypal.me/interventionio", + "type": "custom" + }, + { + "url": "https://github.com/Intervention", + "type": "github" + }, + { + "url": "https://ko-fi.com/interventionphp", + "type": "ko_fi" + } + ], + "time": "2026-05-03T06:04:47+00:00" + }, + { + "name": "intervention/image", + "version": "4.1.2", + "source": { + "type": "git", + "url": "https://github.com/Intervention/image.git", + "reference": "ba4a7cc8042882d479a78b0835f3f0e991e40a71" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/Intervention/image/zipball/ba4a7cc8042882d479a78b0835f3f0e991e40a71", + "reference": "ba4a7cc8042882d479a78b0835f3f0e991e40a71", + "shasum": "" + }, + "require": { + "ext-mbstring": "*", + "intervention/gif": "^5", + "php": "^8.3" + }, + "require-dev": { + "mockery/mockery": "^1.6", + "phpstan/phpstan": "^2.1", + "phpunit/phpunit": "^12.0", + "slevomat/coding-standard": "~8.0", + "squizlabs/php_codesniffer": "^4" + }, + "suggest": { + "ext-exif": "Recommended to be able to read EXIF data properly." + }, + "type": "library", + "autoload": { + "psr-4": { + "Intervention\\Image\\": "src" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Oliver Vogel", + "email": "oliver@intervention.io", + "homepage": "https://intervention.io" + } + ], + "description": "PHP Image Processing", + "homepage": "https://image.intervention.io", + "keywords": [ + "gd", + "image", + "imagick", + "resize", + "thumbnail", + "watermark" + ], + "support": { + "issues": "https://github.com/Intervention/image/issues", + "source": "https://github.com/Intervention/image/tree/4.1.2" + }, + "funding": [ + { + "url": "https://paypal.me/interventionio", + "type": "custom" + }, + { + "url": "https://github.com/Intervention", + "type": "github" + }, + { + "url": "https://ko-fi.com/interventionphp", + "type": "ko_fi" + } + ], + "time": "2026-05-23T06:51:28+00:00" + }, { "name": "laravel-lang/actions", "version": "1.10.2", diff --git a/public/.htaccess b/public/.htaccess index b574a59..97d3e6b 100644 --- a/public/.htaccess +++ b/public/.htaccess @@ -23,3 +23,12 @@ RewriteCond %{REQUEST_FILENAME} !-f RewriteRule ^ index.php [L] + + + php_value upload_max_filesize 20M + php_value post_max_size 20M + php_value memory_limit 512M + + + + diff --git a/public/.user.ini b/public/.user.ini new file mode 100644 index 0000000..e55f623 --- /dev/null +++ b/public/.user.ini @@ -0,0 +1,4 @@ +upload_max_filesize = 20M +post_max_size = 20M +max_execution_time = 300 +memory_limit = 256M