aboutsummaryrefslogtreecommitdiff
path: root/src/Controller/ImageUploadController.php
diff options
context:
space:
mode:
authorpolo <ordipolo@gmx.fr>2026-10-23 00:47:32 +0200
committerpolo <ordipolo@gmx.fr>2026-10-23 00:47:32 +0200
commitf27cca08ff786ceff1870147f6d6bad14d71117f (patch)
treed36597a72025313d6faf4ed228755fa267ebe2f6 /src/Controller/ImageUploadController.php
parentc7309f900ef05bd5cb313110ec159b761af394ec (diff)
downloadcms-f27cca08ff786ceff1870147f6d6bad14d71117f.tar.gz
cms-f27cca08ff786ceff1870147f6d6bad14d71117f.tar.bz2
cms-f27cca08ff786ceff1870147f6d6bad14d71117f.zip
dossiers avec majuscule, namespace pour toutes les classes, autoload psr-4, UserDataService::createZip déplacé dans Backup
Diffstat (limited to 'src/Controller/ImageUploadController.php')
-rw-r--r--src/Controller/ImageUploadController.php194
1 files changed, 194 insertions, 0 deletions
diff --git a/src/Controller/ImageUploadController.php b/src/Controller/ImageUploadController.php
new file mode 100644
index 0000000..4ca9f48
--- /dev/null
+++ b/src/Controller/ImageUploadController.php
@@ -0,0 +1,194 @@
1<?php
2// src/Controller/ImageUploadController.php
3
4declare(strict_types=1);
5
6namespace App\Controller;
7
8use Symfony\Component\HttpFoundation\JsonResponse;
9use App\Service\Security;
10
11class ImageUploadController
12{
13 const ALLOWED_EXTENSIONS = ['jpg', 'jpeg', 'png', 'gif', 'webp', 'tiff', 'tif'];
14
15 static public function imagickCleanAndWriteImage(string $image_data, string $local_path): bool // "string" parce que file_get_contents...
16 {
17 $format = strtolower(pathinfo($local_path)['extension']);
18 try{
19 $imagick = new \Imagick();
20 $imagick->readImageBlob($image_data);
21 $imagick->stripImage(); // nettoyage métadonnées
22 //$imagick->setImageFormat($format); // inutile, writeImage force la conversion
23
24 // compression
25 switch($format){
26 case 'jpeg': // particularité du switch, si 'jpeg' le test de 'jpg' est ignoré et on va jusqu'au break
27 case 'jpg':
28 $imagick->setImageCompression(\Imagick::COMPRESSION_JPEG);
29 $imagick->setImageCompressionQuality(85);
30 break;
31 case 'webp':
32 $imagick->setImageCompression(\Imagick::COMPRESSION_WEBP);
33 $imagick->setImageCompressionQuality(85);
34 break;
35 case 'png':
36 $imagick->setImageCompression(\Imagick::COMPRESSION_ZIP);
37 $imagick->setImageCompressionQuality(7); // 9 est sans perte
38 break;
39 case 'tiff':
40 $imagick->setImageCompression(\Imagick::COMPRESSION_LZW); // LZW est sans perte
41 break;
42 }
43
44 // enregistrement
45 // writeImage utilise l'extension du fichier et ignore le format détecté
46 // imagemagick est à l'origine une appli console, elle considère que l'extension montre l'intention de l'utilisateur
47 $imagick->writeImage($local_path); // enregistrement
48 $imagick->clear();
49 $imagick->destroy();
50 return true;
51 }
52 catch(\Exception $e){
53 return false;
54 }
55 }
56 static public function curlDownloadImage(string $url, int $maxRetries = 3, int $timeout = 10): string|false
57 {
58 $attempt = 0;
59 $imageData = false;
60
61 while($attempt < $maxRetries){
62 $ch = curl_init($url); // instance de CurlHandle
63 curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
64 curl_setopt($ch, CURLOPT_FOLLOWLOCATION, true);
65 curl_setopt($ch, CURLOPT_CONNECTTIMEOUT, $timeout);
66 curl_setopt($ch, CURLOPT_TIMEOUT, $timeout);
67 curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, true);
68 curl_setopt($ch, CURLOPT_USERAGENT, 'TinyMCE-Image-Downloader');
69
70 $imageData = curl_exec($ch);
71 $httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE);
72 //$curlError = curl_error($ch);
73
74 if($imageData !== false && $httpCode >= 200 && $httpCode < 300){
75 return $imageData;
76 }
77
78 $attempt++;
79 sleep(1);
80 }
81
82 return false; // échec après trois tentatives
83 }
84
85 // téléchargement par le plugin (bouton "insérer une image")
86 static public function imageUploadTinyMce(): JsonResponse
87 {
88 if(!isset($_FILES['file'])){
89 return new JsonResponse(['message' => 'Erreur 400: Bad Request'], JsonResponse::HTTP_BAD_REQUEST); // code 400
90 }
91 if(!is_uploaded_file($_FILES['file']['tmp_name'])) {
92 return new JsonResponse(['message' => "Le fichier n'a pas été téléchargé correctement."], JsonResponse::HTTP_BAD_REQUEST); // code 400
93 }
94
95 $dest = 'user_data/images/';
96 $dest_mini = 'user_data/images-mini/';
97
98 // Vérifier si les répertoires existent, sinon les créer
99 if(!is_dir($dest)){
100 mkdir($dest, 0755, true);
101 }
102 if(!is_dir($dest_mini)){
103 mkdir($dest_mini, 0755, true);
104 }
105
106 $name = Security::secureFileName(pathinfo($_FILES['file']['name'], PATHINFO_FILENAME));
107 $extension = strtolower(pathinfo($_FILES['file']['name'], PATHINFO_EXTENSION));
108 $image_data = file_get_contents($_FILES['file']['tmp_name']);
109 if(!in_array($extension, self::ALLOWED_EXTENSIONS)){
110 $extension = 'jpeg';
111 }
112 $local_path = uniqid($dest . $name . '_') . '.' . $extension;
113
114 // créer une miniature de l'image
115 //
116
117 if(self::imagickCleanAndWriteImage($image_data, $local_path)){ // recréer l’image pour la nettoyer
118 return new JsonResponse(['location' => $local_path]); // renvoyer l'URL de l'image téléchargée
119 }
120 else{
121 return new JsonResponse(['message' => 'Erreur image non valide'], JsonResponse::HTTP_INTERNAL_SERVER_ERROR); // code 500
122 }
123 }
124
125 // collage de HTML => recherche de balises <img>, téléchargement côté serveur et renvoi de l'adresse sur le serveur
126 static public function uploadImageHtml(): JsonResponse
127 {
128 $json = json_decode(file_get_contents('php://input'), true);
129
130 if(!isset($json['image_url'])){
131 return new JsonResponse(['message' => "Erreur 400: Bad Request"], JsonResponse::HTTP_BAD_REQUEST); // code 400
132 }
133
134 $image_data = self::curlDownloadImage($json['image_url']); // téléchargement de l’image par le serveur avec cURL au lieu de file_get_contents
135 if(!$image_data){
136 return new JsonResponse(['message' => "Erreur, le serveur n'a pas réussi à télécharger l'image."], JsonResponse::HTTP_BAD_REQUEST); // code 400
137 }
138
139 $dest = 'user_data/images/';
140 if(!is_dir($dest)){ // Vérifier si le répertoire existe, sinon le créer
141 mkdir($dest, 0755, true);
142 }
143
144 $url_path = parse_url($json['image_url'], PHP_URL_PATH);
145 $name = Security::secureFileName(pathinfo($url_path, PATHINFO_FILENAME));
146 $extension = strtolower(pathinfo($url_path, PATHINFO_EXTENSION));
147 if(!in_array($extension, self::ALLOWED_EXTENSIONS) || $extension === 'jpg'){
148 $extension = 'jpeg';
149 }
150 $local_path = uniqid($dest . $name . '_') . '.' . $extension;
151
152 if(self::imagickCleanAndWriteImage($image_data, $local_path)){ // recréer l’image pour la nettoyer
153 return new JsonResponse(['location' => $local_path]); // nouvelle adresse
154 }
155 else{
156 return new JsonResponse(['message' => 'Erreur image non valide', 'format' => $extension], JsonResponse::HTTP_INTERNAL_SERVER_ERROR); // code 500
157 }
158 }
159
160 // collage simple d'une image (base64 dans le presse-papier) non encapsulée dans du HTML
161 static public function uploadImageBase64(): JsonResponse
162 {
163 $json = json_decode(file_get_contents('php://input'), true);
164 $dest = 'user_data/images/';
165
166 if(!is_dir($dest)){
167 mkdir($dest, 0755, true);
168 }
169
170 // détection de data:image/ et de ;base64, et capture du format dans $type
171 if(!isset($json['image_base64']) || !preg_match('/^data:image\/(\w+);base64,/', $json['image_base64'], $type)){
172 return new JsonResponse(['message' => 'Données image base64 manquantes ou invalides'], JsonResponse::HTTP_BAD_REQUEST); // code 400
173 }
174
175 $extension = strtolower($type[1]); // dans (\w+)
176 if(!in_array($extension, self::ALLOWED_EXTENSIONS)){
177 $extension = 'jpeg';
178 }
179
180 $name = 'pasted_image';
181 $image_data = base64_decode(substr($json['image_base64'], strpos($json['image_base64'], ',') + 1)); // découpe la chaine à la virgule puis convertit en binaire
182 if($image_data === false){
183 return new JsonResponse(['message' => 'Décodage base64 invalide'], JsonResponse::HTTP_BAD_REQUEST); // code 400
184 }
185 $local_path = uniqid($dest . $name . '_') . '.' . $extension;
186
187 if(self::imagickCleanAndWriteImage($image_data, $local_path)){
188 return new JsonResponse(['location' => $local_path]);
189 }
190 else{
191 return new JsonResponse(['message' => 'Erreur image non valide', 'format' => $extension], JsonResponse::HTTP_INTERNAL_SERVER_ERROR); // code 500
192 }
193 }
194} \ No newline at end of file