Pas 23: Redimensionarea imaginilor

5.0 version
Maintained

Redimensionarea imaginilor

Pe pagina de conferință, fotografiile sunt limitate la o dimensiune maximă de 200 x 150 pixeli. Cum rămâne cu optimizarea imaginilor și reducerea dimensiunii acestora dacă originalul încărcat este mai mare decât limitele?

Aceasta este o sarcină perfectă care poate fi adăugată la fluxul de lucru al comentariilor, probabil imediat după validarea comentariului și chiar înainte de publicarea acestuia.

Să adăugăm o nouă stare ready și o tranziție optimize:

patch_file
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
--- a/config/packages/workflow.yaml
+++ b/config/packages/workflow.yaml
@@ -16,6 +16,7 @@ framework:
                 - potential_spam
                 - spam
                 - rejected
+                - ready
                 - published
             transitions:
                 accept:
@@ -29,13 +30,16 @@ framework:
                     to:   spam
                 publish:
                     from: potential_spam
-                    to:   published
+                    to:   ready
                 reject:
                     from: potential_spam
                     to:   rejected
                 publish_ham:
                     from: ham
-                    to:   published
+                    to:   ready
                 reject_ham:
                     from: ham
                     to:   rejected
+                optimize:
+                    from: ready
+                    to:   published

Generează o reprezentare vizuală a noii configurații a fluxului de lucru pentru a valida faptul că descrie ceea ce dorim:

1
$ symfony console workflow:dump comment | dot -Tpng -o workflow.png
../_images/workflow-final.png

Optimizarea imaginilor cu Imagine

Optimizarea imaginilor se va face grație lui GD (verifică dacă instalația PHP locală are extensia GD activată) și Imagine:

1
$ symfony composer req imagine/imagine

Redimensionarea unei imagini se poate face prin următoarea clasă de servicii:

src/ImageOptimizer.php
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
namespace App;

use Imagine\Gd\Imagine;
use Imagine\Image\Box;

class ImageOptimizer
{
    private const MAX_WIDTH = 200;
    private const MAX_HEIGHT = 150;

    private $imagine;

    public function __construct()
    {
        $this->imagine = new Imagine();
    }

    public function resize(string $filename): void
    {
        list($iwidth, $iheight) = getimagesize($filename);
        $ratio = $iwidth / $iheight;
        $width = self::MAX_WIDTH;
        $height = self::MAX_HEIGHT;
        if ($width / $height > $ratio) {
            $width = $height * $ratio;
        } else {
            $height = $width / $ratio;
        }

        $photo = $this->imagine->open($filename);
        $photo->resize(new Box($width, $height))->save($filename);
    }
}

După optimizarea fotografiei, stocăm noul fișier în locul celui original. Însă s-ar putea să dorești să păstrezi imaginea originală.

Adăugarea unui nou pas în fluxul de lucru

Modifică fluxul de lucru pentru a gestiona noua stare:

patch_file
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
--- a/src/MessageHandler/CommentMessageHandler.php
+++ b/src/MessageHandler/CommentMessageHandler.php
@@ -2,6 +2,7 @@

 namespace App\MessageHandler;

+use App\ImageOptimizer;
 use App\Message\CommentMessage;
 use App\Repository\CommentRepository;
 use App\SpamChecker;
@@ -21,10 +22,12 @@ class CommentMessageHandler implements MessageHandlerInterface
     private $bus;
     private $workflow;
     private $mailer;
+    private $imageOptimizer;
     private $adminEmail;
+    private $photoDir;
     private $logger;

-    public function __construct(EntityManagerInterface $entityManager, SpamChecker $spamChecker, CommentRepository $commentRepository, MessageBusInterface $bus, WorkflowInterface $commentStateMachine, MailerInterface $mailer, string $adminEmail, LoggerInterface $logger = null)
+    public function __construct(EntityManagerInterface $entityManager, SpamChecker $spamChecker, CommentRepository $commentRepository, MessageBusInterface $bus, WorkflowInterface $commentStateMachine, MailerInterface $mailer, ImageOptimizer $imageOptimizer, string $adminEmail, string $photoDir, LoggerInterface $logger = null)
     {
         $this->entityManager = $entityManager;
         $this->spamChecker = $spamChecker;
@@ -32,7 +35,9 @@ class CommentMessageHandler implements MessageHandlerInterface
         $this->bus = $bus;
         $this->workflow = $commentStateMachine;
         $this->mailer = $mailer;
+        $this->imageOptimizer = $imageOptimizer;
         $this->adminEmail = $adminEmail;
+        $this->photoDir = $photoDir;
         $this->logger = $logger;
     }

@@ -63,6 +68,12 @@ class CommentMessageHandler implements MessageHandlerInterface
                 ->to($this->adminEmail)
                 ->context(['comment' => $comment])
             );
+        } elseif ($this->workflow->can($comment, 'optimize')) {
+            if ($comment->getPhotoFilename()) {
+                $this->imageOptimizer->resize($this->photoDir.'/'.$comment->getPhotoFilename());
+            }
+            $this->workflow->apply($comment, 'optimize');
+            $this->entityManager->flush();
         } elseif ($this->logger) {
             $this->logger->debug('Dropping comment message', ['comment' => $comment->getId(), 'state' => $comment->getState()]);
         }

Reține că $photoDir este injectat automat deoarece am definit un container bind pe această denumire a variabilei într-un pas anterior:

config/packages/services.yaml
1
2
3
4
services:
    _defaults:
        bind:
            $photoDir: "%kernel.project_dir%/public/uploads/photos"

Stocarea datelor încărcate în producție

Am definit deja în .symfony.cloud.yaml un director special cu drepturi de scriere și de citire pentru fișierele încărcate. Dar montarea directorului este locală. Dacă dorim ca atât consumatorul de mesaje, cât și containerul web să poată accesa acel director, trebuie să creăm un serviciu destinat fișierelor:

patch_file
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
--- a/.symfony/services.yaml
+++ b/.symfony/services.yaml
@@ -16,3 +16,7 @@ varnish:
         vcl: !include
             type: string
             path: config.vcl
+
+files:
+    type: network-storage:1.0
+    disk: 256

Folosiți-l pentru directorul de încărcare a fotografiilor:

patch_file
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
--- a/.symfony.cloud.yaml
+++ b/.symfony.cloud.yaml
@@ -29,7 +29,7 @@ disk: 512

 mounts:
     "/var": { source: local, source_path: var }
-    "/public/uploads": { source: local, source_path: uploads }
+    "/public/uploads": { source: service, service: files, source_path: uploads }

 hooks:
     build: |

Acest lucru ar trebui să fie suficient pentru ca noile implementări să funcționeze în producție.


  • « Previous Pas 22: Stilarea interfeței cu Webpack
  • Next » Pas 24: Rulând sarcinile Cron

This work, including the code samples, is licensed under a Creative Commons BY-NC-SA 4.0 license.