[BUGFIX] Decouple thumbnail generation from controllers 82/57682/2
authorMathias Schreiber <mathias.schreiber@typo3.org>
Fri, 20 Apr 2018 19:23:35 +0000 (21:23 +0200)
committerFrank Naegler <frank.naegler@typo3.org>
Thu, 2 Aug 2018 17:51:17 +0000 (19:51 +0200)
Deferred rendering of thumbnails via an external request speed up
the backend performance. This currently works in all places that use
BackendUtility::thumbsCode.

Resolves: #85605
Releases: master, 8.7
Change-Id: Id5635fc9cd340b8bcf21995cd457415aaa1a8926
Reviewed-on: https://review.typo3.org/57682
Tested-by: TYPO3com <no-reply@typo3.com>
Reviewed-by: Frank Naegler <frank.naegler@typo3.org>
Tested-by: Frank Naegler <frank.naegler@typo3.org>
typo3/sysext/backend/Classes/Controller/File/ThumbnailController.php [new file with mode: 0644]
typo3/sysext/backend/Classes/Utility/BackendUtility.php
typo3/sysext/backend/Configuration/Backend/Routes.php

diff --git a/typo3/sysext/backend/Classes/Controller/File/ThumbnailController.php b/typo3/sysext/backend/Classes/Controller/File/ThumbnailController.php
new file mode 100644 (file)
index 0000000..734c13b
--- /dev/null
@@ -0,0 +1,63 @@
+<?php
+declare(strict_types = 1);
+namespace TYPO3\CMS\Backend\Controller\File;
+
+/*
+ * This file is part of the TYPO3 CMS project.
+ *
+ * It is free software; you can redistribute it and/or modify it under
+ * the terms of the GNU General Public License, either version 2
+ * of the License, or any later version.
+ *
+ * For the full copyright and license information, please read the
+ * LICENSE.txt file that was distributed with this source code.
+ *
+ * The TYPO3 project - inspiring people to share!
+ */
+
+use Psr\Http\Message\ResponseInterface;
+use Psr\Http\Message\ServerRequestInterface;
+use TYPO3\CMS\Core\Http\Response;
+use TYPO3\CMS\Core\Resource\File;
+use TYPO3\CMS\Core\Resource\ProcessedFile;
+use TYPO3\CMS\Core\Resource\ResourceFactory;
+use TYPO3\CMS\Core\Utility\ArrayUtility;
+
+/**
+ * Class ThumbnailController
+ */
+class ThumbnailController
+{
+    /**
+     * @param ServerRequestInterface $request
+     * @return ResponseInterface
+     */
+    public function render(ServerRequestInterface $request): ResponseInterface
+    {
+        $fileObject = $this->getFileObjectByCombinedIdentifier($request->getQueryParams()['fileIdentifier']);
+        if (!$fileObject->isMissing()) {
+            $processingInstructions = [
+                'width' => 64,
+                'height' => 64,
+                'crop' => null,
+            ];
+            ArrayUtility::mergeRecursiveWithOverrule($processingInstructions, $request->getQueryParams()['processingInstructions']);
+            $processedImage = $fileObject->process(ProcessedFile::CONTEXT_IMAGECROPSCALEMASK, $processingInstructions);
+            $filePath = $processedImage->getForLocalProcessing(false);
+            return new Response($filePath, 200, [
+                'Content-Type' => $processedImage->getMimeType()
+            ]);
+        }
+        return new Response('', 404);
+    }
+
+    /**
+     * @param string $combinedIdentifier
+     * @return File
+     * @throws \InvalidArgumentException
+     */
+    protected function getFileObjectByCombinedIdentifier(string $combinedIdentifier): File
+    {
+        return ResourceFactory::getInstance()->getFileObjectFromCombinedIdentifier($combinedIdentifier);
+    }
+}
index fd46c05..7aa8517 100644 (file)
@@ -1726,19 +1726,23 @@ class BackendUtility
                 ) {
                     $cropVariantCollection = CropVariantCollection::create((string)$fileReferenceObject->getProperty('crop'));
                     $cropArea = $cropVariantCollection->getCropArea();
-                    $processedImage = $fileObject->process(
-                        ProcessedFile::CONTEXT_IMAGECROPSCALEMASK,
-                        [
-                            'width' => $sizeParts[0],
-                            'height' => $sizeParts[1] . 'c',
-                            'crop' => $cropArea->isEmpty() ? null : $cropArea->makeAbsoluteBasedOnFile($fileReferenceObject)
-                        ]
-                    );
-                    $imageUrl = $processedImage->getPublicUrl(true);
-                    $imgTag = '<img src="' . $imageUrl . '" '
-                        . 'width="' . $processedImage->getProperty('width') . '" '
-                        . 'height="' . $processedImage->getProperty('height') . '" '
-                        . 'alt="' . htmlspecialchars($fileReferenceObject->getName()) . '" />';
+                    $processingInformation = [
+                        'width' => $sizeParts[0],
+                        'height' => $sizeParts[1] . 'c',
+                        'crop' => $cropArea->isEmpty() ? null : $cropArea->makeAbsoluteBasedOnFile($fileReferenceObject)
+                    ];
+                    $imageUrl = (string)GeneralUtility::makeInstance(UriBuilder::class)
+                        ->buildUriFromRoute('thumbnails', [
+                            'fileIdentifier' => $fileObject->getCombinedIdentifier(),
+                            'processingInstructions' => $processingInformation
+                        ]);
+                    $attributes = [
+                        'src' => $imageUrl,
+                        'width' => (int)$sizeParts[0],
+                        'height' => (int)$sizeParts[1],
+                        'alt' => $fileReferenceObject->getName(),
+                    ];
+                    $imgTag = '<img ' . GeneralUtility::implodeAttributes($attributes, true) . '/>';
                 } else {
                     // Icon
                     $imgTag = '<span title="' . htmlspecialchars($fileObject->getName()) . '">'
index 2861c37..406f7a6 100644 (file)
@@ -194,5 +194,11 @@ return [
     'record_edit' => [
         'path' => '/record/edit',
         'target' => Controller\EditDocumentController::class . '::mainAction'
+    ],
+
+    // Thumbnails
+    'thumbnails' => [
+        'path' => '/thumbnails',
+        'target' => Controller\File\ThumbnailController::class . '::render'
     ]
 ];