Skip to content

Zmienianie rozmiaru obrazów

Na stronie konferencji zdjęcia są ograniczone do maksymalnego rozmiaru 200 × 150 px. A co z optymalizacją obrazów i pomniejszaniem ich, jeśli przesłany oryginał jest większy niż ustalony limit?

Jest to doskonałe zadanie, które można dodać do przepływu pracy (ang. workflow) nad komentarzami, prawdopodobnie zaraz po zatwierdzeniu komentarza i tuż przed jego opublikowaniem.

Dodajmy zatem nowy stan: ready oraz przejście: optimize:

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

Wygeneruj wizualną reprezentację nowego przepływu pracy, aby sprawdzić, czy opisuje ona to, czego chcemy:

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

Optymalizowanie obrazów z wykorzystaniem pakietu Imagine

Optymalizacja obrazów zostanie wykonana przy pomocy rozszerzenia GD (sprawdź, czy lokalna instalacja PHP posiada włączone to rozszerzenie) oraz pakietu Imagine:

1
$ symfony composer req "imagine/imagine:^1.2"

Zmiana rozmiaru obrazu może być wykonana za pośrednictwem następującego serwisu:

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);
    }
}

Po zoptymalizowaniu zdjęcia, nowy plik zapisujemy jako oryginalny, jednak jeśli chcesz, możesz zachować również wersję sprzed optymalizacji.

Dodawanie nowego kroku (ang. step) w przepływie pracy (ang. workflow)

Zmodyfikuj przepływ pracy tak, aby obsługiwał nowy stan:

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;
     }

@@ -64,6 +69,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()]);
         }

Zauważ, że zmienna $photoDir jest automatycznie wstrzykiwana, ponieważ zdefiniowaliśmy powiązanie (ang. bind) kontenera z tą zmienną w poprzednim kroku:

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

Przechowywanie przesłanych danych w środowisku produkcyjnym

W pliku .platform.app.yaml domyślnie mamy już skonfigurowany katalog dla przesłanych plików, jednak katalog ten jest zainstalowany (ang. mount) lokalnie. Jeśli chcemy, aby kontener i robotnik (ang. worker) odbierający polecenia mieli dostęp do tego samego katalogu, musimy utworzyć serwis obsługi plików:

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

Użyj go jako katalogu dla przesłanych zdjęć:

1
2
3
4
5
6
7
8
9
10
11
--- a/.platform.app.yaml
+++ b/.platform.app.yaml
@@ -35,7 +35,7 @@ web:

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

 relationships:
     database: "database:postgresql"

To powinno wystarczyć, aby funkcja zadziałała w środowisku produkcyjnym.

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