Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Issue with Image Sizing on Cloudflare R2 Adapter #2

Open
minovc3 opened this issue Nov 7, 2023 · 5 comments
Open

Issue with Image Sizing on Cloudflare R2 Adapter #2

minovc3 opened this issue Nov 7, 2023 · 5 comments
Assignees

Comments

@minovc3
Copy link

minovc3 commented Nov 7, 2023

Hello,

Great adapter! I'm really impressed of the features it has, but at the same time I've got stuck on one of them.

Here's one aspect that I was unable to get functioning or comprehend.

When employing local storage, images set to lazy load typically come from this path example.com/content/images/size/.

However, if you switch to using the cloudflare-r2 storage provider, and enable the RESPONSIVE_IMAGES feature, the images are not delivered from cdn.example.com/content/images/size/ as expected.

Despite this, the URLs for these images still point to cdn.example.com/content/images, which serves up the full-sized images, instead of directing to the specific size path. Without a specific URL prefix for resized images in the configuration, it's not clear how the system differentiates URLs for resized images from the original ones.

On the other hand, at least the images get sized in the R2 bucket

CleanShot 2023-11-07 at 21 24 00@2x

This is the configuration I'm using

"storage": {
        "active": "ghost-cloudflare-r2",
        "ghost-cloudflare-r2": {
          "GHOST_STORAGE_ADAPTER_R2_ENDPOINT":"https://r2.cloudflarestorage.com",
          "GHOST_STORAGE_ADAPTER_R2_ACCESS_KEY_ID": "secret",
          "GHOST_STORAGE_ADAPTER_R2_SECRET_ACCESS_KEY":"secret",
          "GHOST_STORAGE_ADAPTER_R2_BUCKET": "my-ghost-assets",
          "GHOST_STORAGE_ADAPTER_R2_DOMAIN": "https://cdn.example.com",
          "GHOST_STORAGE_ADAPTER_R2_IMAGES_URL_PREFIX": "/content/images/",
          "GHOST_STORAGE_ADAPTER_R2_RESIZE_WIDTHS": "300,600,1000,1600,2000",
          "GHOST_STORAGE_ADAPTER_R2_RESPONSIVE_IMAGES": "true"
        },
        "media": {
            "adapter": "ghost-cloudflare-r2",
            "storage_type_media": true
          },
        "files": {
            "adapter": "ghost-cloudflare-r2",
            "storage_type_files": true
        }

Here are two instances showcasing the same image: one using the adapter and the other using local storage.

###Images Loaded Using Cloudflare R2 Adapter 

<img class="post-hero__img lazyautosizes lazyloaded" data-srcset="https://cdn.example.com/content/images/2023/11/bla.jpg 300w,
                    https://cdn.example.com/content/images/2023/11/bla.jpg 600w,
                    https://cdn.example.com/content/images/2023/11/bla.jpg 1000w,
                    https://cdn.example.com/content/images/2023/11/bla.jpg 2000w" srcset="https://cdn.example.com/content/images/2023/11/bla.jpg 300w,
                    https://cdn.example.com/content/images/2023/11/bla.jpg 600w,
                    https://cdn.example.com/content/images/2023/11/bla.jpg 1000w,
                    https://cdn.example.com/content/images/2023/11/bla.jpg 2000w" data-sizes="auto" data-src="https://cdn.example.com/content/images/2023/11/bla-1.jpg" src="https://cdn.example.com/content/images/2023/11/bla-1.jpg" alt="testing cdn2" sizes="908px">


###Images Loaded Using Ghost local Storage 
<img class="post-hero__img lazyautosizes lazyloaded" data-srcset="/content/images/size/w300/2023/11/bla.jpg 300w,
                    /content/images/size/w600/2023/11/bla.jpg 600w,
                    /content/images/size/w1000/2023/11/bla.jpg 1000w,
                    /content/images/size/w2000/2023/11/bla.jpg 2000w" srcset="/content/images/size/w300/2023/11/bla.jpg 300w,
                    /content/images/size/w600/2023/11/bla.jpg 600w,
                    /content/images/size/w1000/2023/11/bla.jpg 1000w,
                    /content/images/size/w2000/2023/11/bla.jpg 2000w" data-sizes="auto" data-src="/content/images/size/w300/2023/11/bla.jpg" src="/content/images/size/w300/2023/11/bla.jpg" alt="local storage" sizes="908px">

Perhaps there's something I'm overlooking!
I'd appreciate your feedback!
Thank you. 🙏

@egeldenhuys egeldenhuys self-assigned this Nov 9, 2023
@egeldenhuys
Copy link
Owner

Hi @minovc3, thank you for the detailed feedback on the adapter, much appreciated!

What version of Ghost are you using?
Also, which theme are you using?

@minovc3
Copy link
Author

minovc3 commented Nov 9, 2023

Thank you for your patience and quick response 🙏🏻
Alright. Currently, I'm using Ghost version 5.71.2 on Docker. My setup includes the Tuuli theme, here is the documentation.

Below is the snippet of code from this theme that manages the images.

 {{#if feature_image}}
          <figure class="post-hero__figure m-b-lg">
            <img
              class="lazyload post-hero__img"
              data-srcset="{{img_url feature_image size="s"}} 300w,
                      {{img_url feature_image size="m"}} 600w,
                      {{img_url feature_image size="l"}} 1000w,
                      {{img_url feature_image size="xl"}} 1500w"
              srcset="data:image/gif;base64,R0lGODlhAQABAAAAACH5BAEKAAEALAAAAAABAAEAAAICTAEAOw=="
              data-sizes="auto"
              data-src="{{img_url feature_image size="s"}}"
              src="{{img_url feature_image size="xs"}}"
              alt="{{#if feature_image_alt}}{{feature_image_alt}}{{else}}{{title}}{{/if}}"/>
            {{#if feature_image_caption}}
              <figcaption>{{feature_image_caption}}</figcaption>
            {{/if}}
          </figure>
        {{/if}}

Now, I believe I've made some progress. Look, I've noticed that If the URL that comes back is an r2 URL instead of a local {SITE_URL}/content/images URL, then unfortunately, responsive images won’t function.

So Here’s my approach:

I modified the GHOST_STORAGE_ADAPTER_R2_DOMAIN from https://cdn.example.com/ to the local {SITE_URL} blog.example.com.

Following that, I set up a redirect rule in Ghost as follows:

302:
  ^\/content\/images\/(.*)$: https://cdn.example.com/content/images/$1
 

I'm halfway there now.

All the featured images have been successfully redirected and are functioning, but I've hit a snag with the gallery images – none of them are redirecting.

So, as it stands, the featured images are optimized while using the redirect workaround, but the galleries and images within posts are not being redirected.

Here is an example:

This is a post card image with the R2 adapter:

<figure class="kg-card kg-image-card"><a data-no-swup="" data-fslightbox="" href="https://blog.example.com/content/images/2023/11/test.jpg" aria-label="Click for Lightbox"><img src="https://blog.example.com/content/images/2023/11/test.jpg" class="kg-image" alt="" loading="lazy" width="1500" height="1106"></a></figure>

This is a post card image with the local adapter:

<figure class="kg-card kg-image-card"><a data-no-swup="" data-fslightbox="" href="https://blog.example.com/content/images/2023/11/test.jpg" aria-label="Click for Lightbox"><img src="https://blog.example.com/content/images/2023/11/test.jpg" class="kg-image" alt="" loading="lazy" width="2000" height="1475" srcset="https://blog.example.com/content/images/size/w600/2023/11/test.jpg 600w, https://blog.example.com/content/images/size/w1000/2023/11/test.jpg 1000w, https://blog.example.com/content/images/size/w1600/2023/11/test.jpg 1600w, https://blog.example.com/content/images/size/w2400/2023/11/test.jpg 2400w" sizes="(min-width: 720px) 720px"></a></figure>

The redirect doesn't help on the image-card

Don't really know why!

@fritz-fritz
Copy link

fritz-fritz commented Jun 10, 2024

Done a little digging into this. It seems as @minovc3 pointed out, Ghost will not correctly generate the srcset unless ghost detects the image source as being local.

See this thread on the forums https://forum.ghost.org/t/responsive-images-and-custom-storage-adapters/13183
the current codebase has changed since the thread but the logic still exists.

So in my case, I have changed my R2_DOMAIN from cdn.example.com to www.example.com to match the blog and in my cloudflare worker created a trigger route for www.example.com/content/images/ /content/media/ and /content/files. This effectively accomplishes the same as your redirect but at the edge.

What interests me is that he seems to have used Ghost's transform code to generate the resizes. THAT is much wanted and I am looking to see if I can implement as it seems Ghost is now writing the resized files to a different path:

<img srcset="/content/images/size/w160/format/webp/2024/06/default-feature-image-1.jpg 160w,
             /content/images/size/w320/format/webp/2024/06/default-feature-image-1.jpg 320w,
             /content/images/size/w600/format/webp/2024/06/default-feature-image-1.jpg 600w,
             /content/images/size/w960/format/webp/2024/06/default-feature-image-1.jpg 960w,
             /content/images/size/w1200/format/webp/2024/06/default-feature-image-1.jpg 1200w,
             /content/images/size/w2000/format/webp/2024/06/default-feature-image-1.jpg 2000w" sizes="320px"
        src="/content/images/size/w600/2024/06/default-feature-image-1.jpg" alt="Coming soon" loading="lazy">

Relevant Line Here: https://github.com/TryGhost/Ghost/blob/81b46c53ccc40a3805334fdb4850dc6bff507a33/ghost/core/core/frontend/utils/images.js#L99

So it seems Ghost now is converting the images to optimize for the web! Awesome! Let's see if we can import/integrate their functions and make the backend be really invisible!

@fritz-fritz
Copy link

Seems the image-transform code has moved but I found it in the TryGhost/SDK repo.

Useful function here unsafeResizeFromBuffer might be an efficient way to generate the resized images that Ghost expects and then send to the s3 client. It can be called with resizeFromBuffer. It can also transform the file type. So I know we can generate the right images... but I fear the difficult part is going to be getting them generated to the formats Ghost is specifying in the srcset. Somehow, Ghost is able to generate these on the fly so figuring out where/how that happens is my next step. But if that fails, we could just write some logic to convert to all supported formats... but that could make storage costs skyrocket potentially so I'd like to avoid

@fritz-fritz
Copy link

Just an update, I have a local branch I'm working on that successfully pulls the theme sizes and dynamically resizes and pushes via S3 to the correct paths for all formats (which is also dynamically grabbed from ghost's code). But unfortunately, it seems the image and gallery cards don't respect the theme sizes so I am looking now for where that is defined so that I can incorporate it. Once I have it all working right I'll send a pull request for review.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

3 participants