When Converting an Image's Format with Image Styles, Replace the Extension instead of Appending

Created on 9 January 2024, about 1 year ago

Problem/Motivation

Currently, when an image is converted to another format by an image style, the new extension is appended to the file name (instead of the new extension replacing the old extension).

Details

The extension is added by Drupal\image\EntityImageStyle::addExtension(), which also does a pretty good job of explaining what is happening and why:

  /**
   * Adds an extension to a path.
   *
   * If this image style changes the extension of the derivative, this method
   * adds the new extension to the given path. This way we avoid filename
   * clashes while still allowing us to find the source image.
   *
   * @param string $path
   *   The path to add the extension to.
   *
   * @return string
   *   The given path if this image style doesn't change its extension, or the
   *   path with the added extension if it does.
   */
  protected function addExtension($path) {
    $original_extension = pathinfo($path, PATHINFO_EXTENSION);
    $extension = $this->getDerivativeExtension($original_extension);
    if ($original_extension !== $extension) {
      $path .= '.' . $extension;
    }
    return $path;
  }

Then, when an image derivative is requested (assuming it doesn't already exist and would then be served by the web server), the request is first processed by Drupal\image\PathProcessor\PathProcessorImageStyles::processInbound(). This path processor rewrites the request URI and appends a query parameter for the original image file.

If the $path parameter passed to ::processInbound() is '/sites/default/files/styles/blog_article_image_1792x784/public/blog/article-image/image.png.webp', then the method will return
'/sites/default/files/styles/blog_article_image_1792x784/public' with the request modified by adding a 'file' query parameter value of 'blog/article-image/image.png.webp'.

The request is later handled by Drupal\image\Controller\ImageStyleDownloadController::deliver(). The $scheme and $image_style parameters are parsed from the path. (In this example, $scheme is 'public' and $image_style is 'blog_article_image_1792x784'.) The 'file' query parameter is used to infer the original file.

As a result, implementing this feature request will require touching several pieces of code. The issue of namespace collisions will also need addressed. E.g. What happens if image.png and image.jpg are both uploaded? Both can't use the image.webp namespace.

As is detailed in ๐Ÿ› ImageStyleDownloadController::deliver() infers source image when generating derivatives with image format conversion Active , Drupal\image\Controller\ImageStyleDownloadController::deliver() can incorrectly infer the source image, and, as a result, render an incorrect image.

Because this behavior is intentional, I'm filing this issue as a feature request instead of a bug.

Steps to reproduce

  1. Install a vanilla Drupal site with the 'Standard' profile.
  2. Install Media & Media Library modules.
  3. Navigate to /admin/config/media/media-settings and enable the 'Standalone media URL' setting.
  4. Ensure the 'Default' view mode for the 'Image' media type is configured to render the 'Large' image style (/admin/structure/media/manage/image/display).
  5. Update the 'Large' image style to convert to 'WebP' (/admin/config/media/image-styles/manage/large).
  6. Upload a PNG image (e.g. image.png) to a media entity (/media/1).
  7. Load /media/1 and observe the image file name is image.png.webp and not image.webp.

Proposed resolution

TBD

Remaining tasks

TBD

User interface changes

TBD

API changes

TBD

Data model changes

TBD

Release notes snippet

TBD

โœจ Feature request
Status

Active

Version

11.0 ๐Ÿ”ฅ

Component
Image moduleย  โ†’

Last updated 5 days ago

Created by

๐Ÿ‡บ๐Ÿ‡ธUnited States Chris Burge

Live updates comments and jobs are added and updated live.
Sign in to follow issues

Merge Requests

Comments & Activities

  • Issue created by @Chris Burge
  • ๐Ÿ‡ช๐Ÿ‡ธSpain candelas

    Good to learn about this bug. I was going to convert all images to webp.
    I hope you to have time to make a patch.
    Thanks

  • This looks like a bug report rather than a feature request.

  • ๐Ÿ‡ง๐Ÿ‡ชBelgium bramvandenbulcke

    I have been using the conversion to webp functionality since launch (in core). I have seen this behaviour from the beginning. But I thought this was intentional. Apparently not!

  • ๐Ÿ‡ฎ๐Ÿ‡ณIndia pradhumanjainOSL

    pradhumanjain2311 โ†’ made their first commit to this issueโ€™s fork.

  • Pipeline finished with Failed
    12 months ago
    Total: 551s
    #157649
  • ๐Ÿ‡บ๐Ÿ‡ธUnited States w01f

    ChatGPT suggests that handling conversions by replacing, rather than appending, the new extension may be better for SEO and caching. It suggests 2 methods:

    1. Adjust Your Conversion Process
    If youโ€™re using a tool (like ImageMagick, GD, or a dedicated Drupal module) to convert images to WebP, check its configuration. Some tools allow you to specify a naming pattern so that the original extension is replaced rather than appended. For example, instead of appending .webp to DSC_3347.jpeg, youโ€™d configure it to replace .jpeg with .webp.

    2. Use a Drupal Hook to Alter the Output URL
    In Drupal, you can implement a hook (such as hook_image_style_url_alter()) to modify the generated image URL. For example:

    php
    Copy
    /**
     * Implements hook_image_style_url_alter().
     */
    function mymodule_image_style_url_alter(&$url, $style_name, $uri) {
      // Adjust only for styles that convert to WebP.
      if ($style_name == 'your_webp_style') {
        // Replace a filename ending in .anything.webp with just .webp.
        $url = preg_replace('/\.[^.]+\.webp$/', '.webp', $url);
      }
    }

    Replace 'your_webp_style' with the actual machine name of your image style.
    After implementing this hook, clear your caches so the changes take effect.

Production build 0.71.5 2024