Cache busting for resources.Copy

It has been a few days of trying to understand how resources.Copy works but it has been difficult to determine how cache busting works with it. Any ideas?

I do not understand the question.

If you set a fixed filename I think it will have to be the webserver that sets the limit on the freshness of the resource.

Hugo’s mage processing sets a hash which refreshes itself and busts image cache. With resources.copy, nothing like that exists (that I know of) and I noticed my images broke when I run hugo --gc.

What does this mean?

broken links. Don’t know how else I can describe it. Screenshot by Lightshot

Are you getting a 404 response on image when testing locally, or are you only seeing this on live sites?

Both, the last time I tested.

Is the target (new name) of resources.Copy the same as the original path?

Something like this:

{{ with .Resources.Get "cover.jpg" }}
  {{ $base := strings.TrimRight (path.Ext .Key) .Key }}
  {{ with .Resize "300x webp" | fingerprint }}
    {{ $ext := strings.TrimLeft "." (path.Ext .Key) }}
    {{ $target := printf "%s.%d.%s" $base (crypto.FNV32a .Data.Integrity) $ext }}
    {{ with resources.Copy $target . }}
      <img src="{{ .RelPermalink }}" width="{{ .Width }}" height="{{ .Height }}" alt="">
    {{ end }}
  {{ end }}
{{ end }}

Generates cache-busting filenames like this:

public/
β”œβ”€β”€ posts/
β”‚   β”œβ”€β”€ post-1/
β”‚   β”‚   β”œβ”€β”€ cover.3545233311.webp
β”‚   β”‚   β”œβ”€β”€ cover.jpg
β”‚   β”‚   └── index.html
β”‚   └── index.html
β”œβ”€β”€ favicon.ico
└── index.html

The 3545233311 bit in the filename will change whenever you change:

  • The original file name
  • The content of the original file
  • The size of the original file
  • Any of the image processing parameters

I want to combine both JPG and webp, but either one works and the other fails or vice versa. It is related to this topic where I asked the same question, but you told me to experiment, which has not been a success so far (I’ve spent weeks on this). My code so far (for the templates, as I’ve not yet revisited the render hook).

{{- with or (.Resources.GetMatch "cover.*")  (resources.Get "image/thumbnail.jpg") }}
  {{- $base := strings.TrimRight (path.Ext .Key) .Key }}
  {{- $jpgExt := path.Ext .Key }}
  {{- with .Fill (printf "%dx%d webp TopLeft" (div .Width 2) (div .Height 1.45 | int) ) | fingerprint }}
    {{- $webpExt := path.Ext .Key  }}
    {{- $targetImg := printf "%s-%d%s" $base (crypto.FNV32a .Data.Integrity) $jpgExt }}
    {{- $targetWebp := printf "%s-%d%s" $base (crypto.FNV32a .Data.Integrity) $webpExt }}
    {{- with or (resources.Copy $targetWebp .) (resources.Copy $targetImg .)  }}
    <picture>
      <source srcset="{{ .RelPermalink }}" type="image/webp">
      <img  src="{{ $targetImg }}" width="{{ .Width }}" height="{{ .Height }}" alt="" fetchpriority="high">
    </picture>
    {{- end }}
  {{- end }}
{{- end }}

(Here, the webP image works, but the JPG one returns a 404 upon running hugo --gc).

I’ve edited my example to use the correct extension when encoding to another format.

I’ll try to look at your code later today.

Alright. Will appreciate the feedback.

Try this:

git clone --single-branch -b hugo-forum-topic-45006 https://github.com/jmooring/hugo-testing hugo-forum-topic-45006
cd hugo-forum-topic-45006
hugo server

The approach I took is to create a map of the images: the original plus each derivative.

The map looks like:

{
  "jpeg": {},
  "original": {},
  "webp": {}
}

Where each of the {} above is an image resource.

Then I range through the map of images to create the picture element.

layouts/_default/single.html
{{ with .Resources.GetMatch "cover.*" }}
  {{/* Create map of images. */}}
  {{ $iMap := dict "original" . }}
  {{/* Set processing options. */}}
  {{ $formats := slice "webp" "jpeg" }} {{/* Last one is fallback. */}}
  {{ $anchor := "TopLeft" }}
  {{ $targetWidth := div $iMap.original.Width 2 }}
  {{ $targetHeight := div $iMap.original.Height 1.45 | int }}
  {{/* Create new images. */}}
  {{ $base := strings.TrimRight (path.Ext $iMap.original.Key) $iMap.original.Key }}
  {{ range $format := $formats }}
    {{ $opts := printf "%dx%d %s %s" $targetWidth $targetHeight . $anchor }}
    {{ with $iMap.original.Fill $opts | fingerprint }}
      {{ $ext := strings.TrimLeft "." (path.Ext .Key) }}
      {{ $targetPath := printf "%s.%d.%s" $base (crypto.FNV32a .Data.Integrity) $ext }}
      {{ with resources.Copy $targetPath . }}
        {{ $iMap = merge $iMap (dict $format . ) }}
      {{ end }}
    {{ end }}
  {{ end }}
  {{/* Render the picture element. */}}
  <picture>
    {{ range $formats }}
      {{ with index $iMap . }}
        <source srcset="{{ .RelPermalink }}" type="{{ .MediaType.Type }}">
      {{ end }}
    {{ end }}
    {{ with index $iMap (index $formats (sub (len $formats) 1)) }}
      <img src="{{ .RelPermalink }}" width="{{ .Width }}" height="{{ .Height }}" alt="">
    {{ end }}
  </picture>
{{ end }}

This produces:

public/
β”œβ”€β”€ posts/
β”‚   β”œβ”€β”€ post-1/
β”‚   β”‚   β”œβ”€β”€ cover.1906466728.jpg
β”‚   β”‚   β”œβ”€β”€ cover.2036869910.webp
β”‚   β”‚   β”œβ”€β”€ cover.jpg
β”‚   β”‚   └── index.html
β”‚   └── index.html
β”œβ”€β”€ favicon.ico
└── index.html

A side benefit… you can easily wrap the picture element with:

<a href="{{ $iMap.original.RelPermalink }}">
  ...
</a>
3 Likes

Phenomenal! This works just as I expected. Thank you!

1 Like

This topic was automatically closed 2 days after the last reply. New replies are no longer allowed.