Question About Functionality

Created on 9 June 2023, over 1 year ago
Updated 18 July 2023, over 1 year ago

Our site suffers a performance hit when using Brandfolder as a CDN and pulling in large unoptimized images. Is this module able to download images from the Brandfolder CDN and store them in the webserver's local filesystem, then serve them from there? Or does it only map entity URLs to brandfolder CDN links and display them that way?

πŸ’¬ Support request
Status

Fixed

Version

2.0

Component

Documentation

Created by

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

Comments & Activities

  • Issue created by @cbrand02
  • πŸ‡ΊπŸ‡ΈUnited States ndewhurst USA

    Hi cbrand02, are you currently using the module to integrate Brandfolder images with Drupal, or are you using another means of placing Brandfolder images (with Brandfolder CDN URLs) on your site?

    This module does a lot of work to avoid downloading images to the Drupal site's local filesystem so that all images are served from the Brandfolder CDN (for various reasons including governance) *but* also provide fully functional Drupal managed file entries, image fields, image toolkit, etc. so that images associated with Brandfolder-based media entities can be displayed using Drupal image styles and can do most other things that local images can do (except get served via your CDN of choice).

    I'm optimistic that you'd see performance improve quite a bit by displaying Brandfolder images processed by Drupal image styles (including responsive image styles) to enforce basic resolution constraints. There are also a couple additional settings that can improve BF CDN performance depending on image type, and I'm planning to tie those into the Drupal module.

    Maybe we can connect more to talk about your specific use case and needs.

    Cheers,

    Nathanael

  • Hi Nathanael,

    Our site utilizes a custom theme that completely overwrites the templates for different types of content, primarily through the Paragraphs module. In order to retrieve the URL from the Brandfolder CDN to use in these templates we have a custom service with this function:

      /**
       * Get Brandfolder image url using BF imagefactory.
       */
      public static function getUrl($media) {
        $field_id = 'field_brandfolder_attachment_id';
    
        if (!empty($media) && $media->hasField($field_id) && !$media->get($field_id)->isEmpty()) {
    
          $bf_id = $media->get($field_id)->getValue()[0]['value'];
          $fid = brandfolder_map_attachment_to_file($bf_id);
    
          $file = File::load($fid);
          $url = $file->createFileUrl();
    
          return $url;
    
        }
    
        /* Return non-BF asset url. */
        return file_create_url($media->field_media_image->entity->uri->value);
      }
    

    This service is then called in preprocess hooks to pass the URLs to templates to be displayed in img elements. The idea is that content authors can upload images to Brandfolder and select them as media when choosing images for paragraphs. However this approach is causing some technical overhead and seems to be bypassing any sort of image optimization that may traditionally occur with this module, resulting in 1mb+ image downloads from https://cdn.bfldr.com.

    Would love to discuss our use case more in depth to see if there's a better way to accomplish this.

    Thanks,
    Chase

  • Hi Nathanael,

    Our site utilizes a custom theme that completely overwrites the templates for different types of content, primarily from the Paragraphs module. In order to retrieve the URL from the Brandfolder CDN to use in these templates we have a custom service with this function:

      /**
       * Get Brandfolder image url using BF imagefactory.
       */
      public static function getUrl($media) {
        $field_id = 'field_brandfolder_attachment_id';
    
        if (!empty($media) && $media->hasField($field_id) && !$media->get($field_id)->isEmpty()) {
    
          $bf_id = $media->get($field_id)->getValue()[0]['value'];
          $fid = brandfolder_map_attachment_to_file($bf_id);
    
          $file = File::load($fid);
          $url = $file->createFileUrl();
    
          return $url;
    
        }
    
        /* Return non-BF asset url. */
        return file_create_url($media->field_media_image->entity->uri->value);
      }
    

    This service is then called in preprocess hooks to pass the URLs to templates to be displayed in img elements. The idea is that content authors can upload images to Brandfolder and select them as media when choosing images for paragraphs. However this approach is causing some technical overhead and seems to be bypassing any sort of image optimization that may traditionally occur with this module, resulting in 1mb+ image downloads from https://cdn.bfldr.com.

    Would love to discuss our use case more in depth to see if there's a better way to accomplish this.

    Thanks,
    Chase

  • Status changed to Needs review over 1 year ago
  • πŸ‡ΊπŸ‡ΈUnited States ndewhurst USA

    Hi Chase,

    Probably the simplest improvement would be to:

    1. Create an image style to constrain images - e.g. scale them down to the max possible output width
    2. Update your code to get a URL for the corresponding style derivative (see below)
    // ...
    if ($fid = brandfolder_map_attachment_to_file($bf_id)) {
      $file = File::load($fid);
      $image_uri = $file->getFileUri();
      $style = ImageStyle::load('my_constraining_style');
      $url = $style->buildUrl($image_uri);
    }
    // ...
    

    Your image style of choice could also use something like a "Focal Point Scale and Crop" effect, in which case you could set a focal point on the image field when editing the BF media entity, and that would be respected when generating the BF CDN URL in the above code.

    Depending on your theming/styling scenario, you could also set up a responsive image style and use the associated picture tag/markup in your component to further optimize per screen size.

    You can also append these params to the BF URL: quality=80&auto=webp, which will (a) compress lossy images at 80 percent and (b) deliver a webp version of the image if a given request indicates that the browser supports that.

    Hopefully some combination of the above will help improve things quite a bit!

  • Hi Nathanael,

    Adding in those params to the BF URL took a >1MB image down to <200kB! Seeing similar results across the board. Thanks for the help, that was a huge win. We will implement something like your other suggestion as well.

    We were also curious, what is the intention of Webhook integration if the images aren't being downloaded?

    Thanks,
    Chase

  • Status changed to Fixed over 1 year ago
  • πŸ‡ΊπŸ‡ΈUnited States ndewhurst USA

    Great! Happy to hear it, and I expect you'll see a further drop with downscaling depending on what your presentation scenarios look like.

    The webhook listener allows any module to subscribe to asset create/update/delete events. The Brandfolder module itself currently subscribes to asset update events in order to fetch relevant metadata that's mapped to media properties/fields, and populate data like alt text for image fields in Drupal if there's new data in Brandfolder for that.
    We might add a built-in feature whereby you can configure proactive creation of Drupal media entities when BF assets are created, which would use the webhook listener.
    You can subscribe to those webhook events in order to implement custom logic, e.g. find all Drupal nodes using a recently-deleted BF asset as their hero image and replace the image with a topically relevant alternative image, notify certain Drupal users, etc.

  • πŸ‡ΊπŸ‡ΈUnited States ndewhurst USA

    cbrand02 P.S. feel free to contact me if you want to talk more about other functionality, use cases, or some of those custom work possibilities.

  • πŸ‡ΊπŸ‡ΈUnited States wesleymusgrove

    Hi @ndewhurst,

    Thanks for the quick response and helpful tips!

    Related to @cbrand02's questions, I'm looking at this article https://medium.com/geekculture/responsive-image-optimization-with-media-... and am curious if you could provide any feedback on how the Brandfolder module could be used in conjunction with the Easy Responsive Images β†’ module regarding defining and using a set of responsive image styles defined in Drupal?

    Also if we're rendering Brandfolder images in our own custom twig templates, how do you recommend that be integrated with Twig Tweak β†’ and the ability to specify attributes that control whether the image is lazy loaded or eagerly preloaded ✨ Add lazy loading example in Cheat Sheet Fixed , i.e. for important images that need priority for Google Core Web vitals and Largest Contentful Paint reasons?

    Thanks!
    Wesley

  • Automatically closed - issue fixed for 2 weeks with no activity.

  • Status changed to Fixed over 1 year ago
  • πŸ‡ΊπŸ‡ΈUnited States ndewhurst USA

    Hi Wesley,

    I haven't used those modules [with Brandfolder], but here are my initial thoughts:

    Re: Easy Responsive Images

    It looks like the main benefits of using that module over the core Responsive Image module are:

    1. Auto-generation of image styles
    2. Twig filter if you need/want to be working in Twig templates when it comes to image rendering
    3. JS to choose images based on container rather than viewport width

    At first glance it looks like this might work just fine with the Brandfolder module. You would create view modes for your Brandfolder-Image-Sourced media types as instructed, then, in the Twig template, just be sure to replace references to `field_media_image` with `bf_image` since that's the name of the image field on each BF media entity.

    Re: Twig Tweak

    If you can do this:

    {# Render image using 'thumbnail' image style and "lazy" loading by attribute. #}
    {{ drupal_image('public://ocean.jpg', 'thumbnail', {loading: 'lazy'}) }}
    

    Then you should be able to do this:

    {# Render image using 'thumbnail' image style and "lazy" loading by attribute. #}
    {{ drupal_image('bf://ATEV5E6X/at/ccs81479tbnkr6gmfpbskwp/ocean.jpg', 'thumbnail', {loading: 'lazy'}) }}
    

    (i.e. using the base URI for the Brandfolder file, and specifying any valid image style)

    Let me know how you fare/fared.

    -Nathanael

  • πŸ‡ΊπŸ‡ΈUnited States wesleymusgrove

    Hi @ndewhurst

    I wanted to provide an update based on what one of my colleagues is experiencing. It seems like the `drupal_image` Twig Tweak function does not support the `bf://uri-to-bf-image` protocol and only allows images to load from `public://uri-to-sites-default-files-image`. This might be related to an existing issue where stream wrappers are needed to load any image outside of the normal `public://` directory?

    See:
    https://www.drupal.org/forum/support/module-development-and-code-questio... β†’
    https://www.drupal.org/project/drupal/issues/1308152 ✨ Add stream wrappers to access extension files Needs work

    However, if we use the file id `fid` of the mirrored Drupal entity Brandfolder image reference, `drupal_image` is able to load that. In tandem with responsive images, this creates a picture tag with a bunch of responsive options for the image size.

    Because of this, we are thinking we'll likely need to create some sort of twig function or preprocess hook that takes a Brandfolder asset id and turns it into an entity id.

    It's either that or we try to use the Imagecache External β†’ Twig filter function to load the absolute URLs of Brandfolder images from the CDN link.

    I'm interested in your thoughts on that approach and what the pros/cons are of not using the `bf://uri-to-bf-image` protocol?

    Thanks!

Production build 0.71.5 2024