步骤 23: 调整图片尺寸

5.0 version
Maintained

调整图片尺寸

在会议页面的设计中,照片被限定为不超过 200 像素宽和 150 像素高。如果上传的原始图片比这个尺寸大,那我们如何来优化图片,调整它们的大小,怎么样?

这个工作是加入评论工作流的不二之选,很可能把它加在评论验证之后,发布它之前。

我们来添加 ready 状态和 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

为这个新的工作流配置生成一个图形,用它来验证这是我们想要的流程:

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

用 Imagine 来优化图片

图片优化需要用 GD (检查你本地安装的PHP是否启用了 GD 扩展)和 Imagine

1
$ symfony composer req imagine/imagine

可以用下面这个服务类来调整图片大小:

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

图片优化完成后,我们存储新的图片文件来代替原始图片。但你可能也会想要在某个地方保留原始图片。

在工作流里增加一个新步骤

修改工作流,让它可以处理新的状态:

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

注意 $photoDir 是自动注入的,因为在之前的步骤中我们已经在容器的 bind 配置里绑定了这个变量名:

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

在生产环境中存储上传的数据

我们已经在 .symfony.cloud.yaml 里为上传文件定义了一个特殊的可读写目录。但是挂载是本地的。如果我们想要让 web 容器和消息消费者的 worker 进程也可以访问同一个挂载,我们需要创建一个 文件服务

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

把它用于照片上传目录:

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: |

在生产环境中,这么做就足以让该功能正常运行了。


  • « Previous 步骤 22: 用 Webpack 来为用户界面设置样式
  • Next » 步骤 24: 运行 cron 定时任务

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