↓Zum Hauptinhalt springen
  1. Artikel/

Umfassender Leitfaden zur Integration von Cloudflare R2-Bildern in Hugo

·2370 Wörter·12 min
Webentwicklung Hugo Cloudflare R2 CDN
Kyo
Autor
Kyo
0.5x~2x
Inhaltsverzeichnis

Einbetten von R2-Bildern in Hugo Markdown-Inhalte
#

Diese Methode ermöglicht es Ihnen, die Standard-Markdown-Syntax für Bilder zu verwenden, die dann automatisch aus Ihrem Cloudflare R2-Bucket geladen werden.

1. Hugo für den R2-Zugriff konfigurieren
#

Definieren Sie zuerst Ihre öffentliche/benutzerdefinierte R2-Domain und ein spezielles lokales Präfix in Ihrer Hugo-Konfigurationsdatei (hugo.toml oder config.yaml). Dieses Präfix signalisiert, welche Bilder in Ihrem Markdown von R2 bezogen werden sollen.

hugo.toml-Beispiel:

[params]
  # Ihre anderen Parameter...
  cdnBaseURL = "https://img.heykyo.com"  # Ersetzen durch Ihre öffentliche/benutzerdefinierte R2-Domain
  cdnLocalAssetPrefix = "/cdn-assets/"     # Das in Markdown für R2-Bilder verwendete Präfix

Für das Blowfish-Theme (mit params.toml):

# Fügen Sie das Folgende in params.toml hinzu...
cdnBaseURL = "https://img.heykyo.com"  # Ersetzen durch Ihre öffentliche/benutzerdefinierte R2-Domain
cdnLocalAssetPrefix = "/cdn-assets/"     # Das in Markdown für R2-Bilder verwendete Präfix

config.yaml-Beispiel:

params:
  # Ihre anderen Parameter...
  cdnBaseURL: "https://img.heykyo.com" # Ersetzen durch Ihre öffentliche/benutzerdefinierte R2-Domain
  cdnLocalAssetPrefix: "/cdn-assets/"    # Das in Markdown für R2-Bilder verwendete Präfix

2. Die Vorlage für das Bild-Rendering überschreiben
#

Um diese R2-Integration zu ermöglichen, müssen Sie das standardmäßige Markdown-Bild-Rendering von Hugo überschreiben. Erstellen Sie eine Datei in Ihrem Hugo-Projekt unter layouts/_default/_markup/render-image.html. Wenn Ihr Theme (wie Blowfish) diese Datei bereits hat, kopieren Sie sie aus dem Pfad layouts/_default/_markup/render-image.html des Themes in den identischen Pfad Ihres Projekts, um sicherzustellen, dass Sie dessen Funktionalität erweitern.

Fügen Sie den folgenden Code in Ihre layouts/_default/_markup/render-image.html ein:

{{- /* layouts/_default/_markup/render-image.html */ -}}

{{- $disableImageOptimization := .Page.Site.Params.disableImageOptimization | default false }}
{{- $url := urls.Parse .Destination }}
{{- $altText := .Text }}
{{- $caption := .Title }}

{{- /* ---- CDN-Konfiguration ---- */ -}}
{{- $cdnBaseURL := site.Params.cdnBaseURL | default "" }}
{{- $cdnLocalAssetPrefix := site.Params.cdnLocalAssetPrefix | default "/cdn-assets/" }}
{{- /* ---- Ende CDN-Konfiguration ---- */ -}}

{{- $isRemoteImage := findRE `^https?` $url.Scheme }}
{{- $isCdnPath := and (not $isRemoteImage) (hasPrefix $url.Path $cdnLocalAssetPrefix) }}

{{- if $isRemoteImage }}
  {{- /* Externes Bild, direkt verwenden */ -}}
  <figure>
    <img class="my-0 rounded-md" loading="lazy" src="{{ $url.String }}" alt="{{ $altText }}" />
    {{ with $caption }}<figcaption>{{ . | markdownify }}</figcaption>{{ end }}
  </figure>
{{- else if and $cdnBaseURL $isCdnPath }}
  {{- /* Lokaler Pfad, der mit dem CDN-Präfix beginnt: umschreiben, um cdnBaseURL zu verwenden */ -}}
  {{- $cdnRelativePath := strings.TrimPrefix $cdnLocalAssetPrefix $url.Path }}
  {{- /* Sicherstellen einer robusten Handhabung von Schrägstrichen zwischen Basis-URL und relativem Pfad */ -}}
  {{- $finalCdnSrc := printf "%s/%s" (strings.TrimSuffix "/" $cdnBaseURL) (strings.TrimPrefix "/" $cdnRelativePath) }}
  <figure>
    <img class="my-0 rounded-md" loading="lazy" src="{{ $finalCdnSrc }}" alt="{{ $altText }}" />
    {{ with $caption }}<figcaption>{{ . | markdownify }}</figcaption>{{ end }}
  </figure>
{{- else }}
  {{- /* Lokaler Pfad, kein CDN-Präfix: Die ursprüngliche Logik des Blowfish-Themes für lokale Ressourcen verwenden */ -}}
  {{- $resource := "" }}
  {{- if $.Page.Resources.GetMatch ($url.String) }}
    {{- $resource = $.Page.Resources.GetMatch ($url.String) }}
  {{- else if resources.GetMatch ($url.String) }}
    {{- $resource = resources.Get ($url.String) }}
  {{- end }}
  {{- with $resource }}
    <figure>
      {{- if or $disableImageOptimization (eq .MediaType.SubType "svg") }}
      <img
        class="my-0 rounded-md"
        loading="lazy"
        src="{{ .RelPermalink }}"
        alt="{{ $altText }}"
      />
      {{- else }}
      <img
        class="my-0 rounded-md"
        loading="lazy"
        srcset="
        {{ (.Resize "330x").RelPermalink }} 330w,
        {{ (.Resize "660x").RelPermalink }} 660w,
        {{ (.Resize "1024x").RelPermalink }} 1024w,
        {{ (.Resize "1320x").RelPermalink }} 2x"
        data-zoom-src="{{ (.Resize "1320x").RelPermalink }}"
        src="{{ (.Resize "660x").RelPermalink }}"
        alt="{{ $altText }}"
      />
      {{- end }}
      {{ with $caption }}<figcaption>{{ . | markdownify }}</figcaption>{{ end }}
    </figure>
  {{- else }}
    {{- /* Lokaler Pfad, aber Ressource von Hugo nicht gefunden. Pfad wie besehen ausgeben (relativ zur Website). */ -}}
    <figure>
      <img class="my-0 rounded-md" loading="lazy" src="{{ $url.String | absURL }}" alt="{{ $altText }}" />
      {{ with $caption }}<figcaption>{{ . | markdownify }}</figcaption>{{ end }}
    </figure>
  {{- end }}
{{- end }}

Dieser Code prüft, ob ein Bildpfad mit Ihrem definierten cdnLocalAssetPrefix beginnt. Wenn ja, konstruiert er die vollständige URL unter Verwendung Ihrer cdnBaseURL; andernfalls greift er auf die Standardbehandlung des Themes für lokale Bilder zurück.

3. Verwendung in Markdown
#

Um ein Bild aus Ihrem R2-Bucket in Ihren Markdown-Inhalt einzufügen, verwenden Sie die Standard-Markdown-Bildsyntax ![Alt-Text](...), aber beginnen Sie den Bildpfad mit Ihrem cdnLocalAssetPrefix. Der Rest des Pfades sollte genau dem Pfad zum Bild in Ihrem R2-Bucket entsprechen (relativ zur cdnBaseURL).

Beispiel:

Dies ist Text in meinem Beitrag.

![Ein Bild aus dem R2-Bucket](/cdn-assets/photos/landscapes/sunset.jpg "Optionale Bildunterschrift hier")

Weiterer Text folgt.

Angenommen, Ihre cdnBaseURL ist https://img.heykyo.com, wird dieses Markdown im finalen HTML wie folgt gerendert:

<img src="https://img.heykyo.com/photos/landscapes/sunset.jpg" alt="Ein Bild aus dem R2-Bucket" ... />

Bilder in Markdown, die nicht mit /cdn-assets/ beginnen, werden von Hugo und Ihrem Theme als reguläre lokale Bilder verarbeitet.

4. Wichtiger Hinweis zu Bildformaten
#

Stellen Sie sicher, dass die in R2 gespeicherten Bilder in webfreundlichen Formaten vorliegen, die von Browsern weitgehend unterstützt werden, wie z. B. JPEG, PNG, WebP, GIF oder SVG. Formate wie HEIC haben eine begrenzte Browserkompatibilität und werden möglicherweise nicht allen Besuchern angezeigt. Es ist am besten, HEIC-Bilder vor dem Hochladen zu konvertieren oder einen Dienst zu nutzen, der sie bei Bedarf spontan konvertieren kann.

Verwendung externer R2-Bilder für Blowfish-Theme-Elemente (Karten & Hintergründe)
#

Das Hugo-Blowfish-Theme bietet eine ausgezeichnete integrierte Unterstützung für “Featured Images” in Artikelkarten und Hero-Abschnitten. Standardmäßig erwartet es, dass diese Bilder (z. B. featured.*, background.*) lokale Ressourcen im Ordner Ihres Beitrags sind. Dieser Abschnitt erklärt, wie Sie die Blowfish-Theme-Partials so ändern, dass Bilder, die auf Cloudflare R2 gehostet werden, für diese Elemente priorisiert werden, während die Fallback-Logik für lokale Bilder erhalten bleibt.

1. Neuen Front-Matter-Parameter definieren
#

Führen Sie in der Front Matter Ihres Markdown-Beitrags einen neuen Parameter ein, featuredImageExternal, um eine R2-URL für Theme-Elemente anzugeben.

Beispiel (YAML Front Matter):

---
title: "Mein Beitrag mit R2-Bildern"
date: 2025-05-28
featuredImageExternal: "https://your-r2-domain.com/path/to/card-or-featured-image.jpg"
tags: ["example"]
---

2. Ändern der Artikelkarten-Bilder
#

Um ein externes R2-Bild für die Artikelübersichtskarten auf Listenseiten zu verwenden:

  • Zu ändernde Datei: layouts/partials/article-link/card.html
  • Aktion: Kopieren Sie die ursprüngliche Datei themes/blowfish/layouts/partials/article-link/card.html in den Pfad layouts/partials/article-link/card.html Ihres Projekts. Bearbeiten Sie die Kopie in Ihrem Projekt.

Geänderte layouts/partials/article-link/card.html:

Die entscheidende Änderung besteht darin, nach .Params.featuredImageExternal zu suchen, bevor die ursprüngliche Bildfindungslogik des Themes ausgeführt wird.

{{ $disableImageOptimization := .Page.Site.Params.disableImageOptimization | default false }}
{{ $bgImageUrl := "" }} {{/* Variable für die Hintergrundbild-URL initialisieren */}}
{{ $hideImage := .Params.hideFeatureImage | default false }} {{/* Prüfen, ob das Bild ausgeblendet werden soll */}}

{{ if not $hideImage }} {{/* --- Nur fortfahren, wenn das Bild nicht ausgeblendet ist --- */}}

  {{/* 1. PRIORISIEREN: Nach externer URL für das Featured Image suchen */}}
  {{ with .Params.featuredImageExternal }}
    {{ $bgImageUrl = . }} {{/* Die externe URL direkt verwenden */}}
  {{ else }}
    {{/* 2. FALLBACK: Wenn keine externe URL vorhanden ist, die ursprüngliche Blowfish-Logik verwenden, um ein LOKALES Bild zu finden */}}
    {{ $images := $.Resources.ByType "image" }}
    {{ $featured := $images.GetMatch "*feature*" }}
    {{ if not $featured }}{{ $featured = $images.GetMatch "{*cover*,*thumbnail*}" }}{{ end }}
    {{/* Handhabung für '.Params.featureimage' - Dies scheint komplex zu sein, beinhaltet möglicherweise einen Remote-Fetch. */}}
    {{/* Wir behalten es bei, beachten aber, dass die Bildoptimierung nicht angewendet wird, wenn hier eine Remote-Ressource geholt wird */}}
    {{ if and .Params.featureimage (not $featured) }}
      {{ $url := .Params.featureimage }}
      {{/* Versuchen, die Ressource abzurufen/zu bekommen. Potenzielle Fehler bei Bedarf behandeln. */}}
      {{/* Dies könnte je nach URL/Setup eine lokale oder eine Remote-Ressource zurückgeben */}}
      {{ $featured = resources.GetRemote $url | default (resources.Get $url) }}
    {{ end }}
    {{/* Website-Standard als nächster Fallback */}}
    {{ if not $featured }}{{ with .Site.Params.defaultFeaturedImage }}{{ $featured = resources.Get . }}{{ end }}{{ end }}

    {{/* Die $featured-Ressource verarbeiten, falls gefunden (es sollte eine lokale Ressource oder von GetRemote behandelt sein) */}}
    {{ with $featured }}
      {{/* Prüfen, ob es sich um ein SVG handelt oder die Optimierung deaktiviert ist */}}
      {{ if or $disableImageOptimization (strings.HasSuffix .RelPermalink ".svg") }}
        {{ $bgImageUrl = .RelPermalink }} {{/* Ursprünglichen relativen Permalink verwenden */}}
      {{ else }}
        {{/* Größenänderung auf das lokale Bild anwenden */}}
        {{ with .Resize "600x" }}
          {{ $bgImageUrl = .RelPermalink }} {{/* Geänderten relativen Permalink verwenden */}}
        {{ end }}
      {{ end }}
    {{ end }} {{/* Ende with $featured */}}
  {{ end }} {{/* Ende else (keine externe URL) */}}

{{ end }} {{/* --- Ende if not $hideImage --- */}}


{{/* --- Link-Struktur --- */}}
{{ with .Params.externalUrl }}
<a href="{{ . }}" target="_blank" rel="external" class="min-w-full">
{{ else }}
<a href="{{ .RelPermalink }}" class="min-w-full">
{{ end }}
  <div class="min-h-full border border-neutral-200 dark:border-neutral-700 border-2 rounded overflow-hidden shadow-2xl relative">

    {{/* --- Das Hintergrundbild mit der ermittelten $bgImageUrl anzeigen --- */}}
    {{ with $bgImageUrl }}
      {{/* Die ermittelte URL (extern oder verarbeitet lokal) verwenden */}}
      <div class="w-full thumbnail_card nozoom" style="background-image:url({{ . | absURL }});"></div>
    {{ else }}
      {{/* Optional: Einen Platzhalter hinzufügen oder leer lassen, wenn kein Bild gefunden und nicht ausgeblendet wurde */}}
      {{/* Beispiel: <div class="w-full thumbnail_card nozoom bg-gray-200 dark:bg-gray-700"></div> */}}
    {{ end }}

    {{/* --- Restlicher Inhalt der Karte (Entwurfs-Label, Titel, Meta, Zusammenfassung) --- */}}
    {{/* HINWEIS: Die ursprünglichen og:image-Meta-Tags wurden hier entfernt, da sie besser in den Head-Partials gehandhabt werden */}}

    {{ if and .Draft .Site.Params.article.showDraftLabel }}
    <span class="absolute top-0 right-0 m-2">
      {{ partial "badge.html" (i18n "article.draft" | emojify) }}
    </span>
    {{ end }}

    <div class="px-6 py-4">
      {{ with .Params.externalUrl }}
      <div>
        <div
          class="font-bold text-xl text-neutral-800 decoration-primary-500 hover:underline hover:underline-offset-2 dark:text-neutral">
          {{ $.Title | emojify }}
          <span class="text-xs align-top cursor-default text-neutral-400 dark:text-neutral-500">
            <span class="rtl:hidden">↗</span>
            <span class="ltr:hidden">↖</span>
          </span>
        </div>
      </div>
      {{ else }}
      <div class="font-bold text-xl text-neutral-800 decoration-primary-500 hover:underline hover:underline-offset-2 dark:text-neutral"
        href="{{ .RelPermalink }}">{{ .Title | emojify }}</div>
      {{ end }}

      <div class="text-sm text-neutral-500 dark:text-neutral-400">
        {{ partial "article-meta/basic.html" . }}
      </div>

      {{ if .Params.showSummary | default (.Site.Params.list.showSummary | default false) }}
      <div class="py-1 prose dark:prose-invert">
        {{ .Summary | plainify }}
      </div>
      {{ end }}
    </div>
    <div class="px-6 pt-4 pb-2">
      {{/* Platzhalter für potenziellen Footer-Inhalt innerhalb der Karte */}}
    </div>
  </div> {{/* Ende des Haupt-Karten-Divs */}}
</a> {{/* Ende des Links */}}

3. Ändern der Hero-Hintergrundbilder
#

Blowfish bietet mehrere Hero-Stile. Wir konzentrieren uns als Beispiel auf die Änderung des background.html-Stils. Sie können ein ähnliches Muster auf andere Hero-Stile anwenden.

  • Zu ändernde Datei: layouts/partials/hero/background.html
  • Aktion: Kopieren Sie themes/blowfish/layouts/partials/hero/background.html in den Pfad layouts/partials/hero/background.html Ihres Projekts und bearbeiten Sie die Kopie in Ihrem Projekt.

Geänderte layouts/partials/hero/background.html:

{{ $disableImageOptimization := .Page.Site.Params.disableImageOptimization | default false }}
{{ $bgImageUrl := "" }} {{/* Variable für die endgültige Hintergrundbild-URL initialisieren */}}

{{/* 1. PRIORISIEREN: Nach externer URL für das Featured Image in der Front Matter suchen */}}
{{ with .Params.featuredImageExternal }}
  {{ $bgImageUrl = . }} {{/* Die externe URL direkt verwenden */}}
{{ else }}
  {{/* 2. FALLBACK: Wenn keine externe URL vorhanden ist, die ursprüngliche Blowfish-Logik verwenden, um eine Bildressource zu finden */}}
  {{ $featured := "" }} {{/* $featured in diesem Geltungsbereich initialisieren */}}
  {{ $images := .Resources.ByType "image" }}
  {{ $featured = $images.GetMatch "*background*" }}
  {{ if not $featured }}{{ $featured = $images.GetMatch "*feature*" }}{{ end }}
  {{ if not $featured }}{{ $featured = $images.GetMatch "{*cover*,*thumbnail*}" }}{{ end }}

  {{/* Handhabung von .Params.featureimage (potenziell remote) */}}
  {{ if and .Params.featureimage (not $featured) }}
    {{ $url := .Params.featureimage }}
    {{/* Standardmäßig "" verwenden, wenn GetRemote fehlschlägt oder wenn Get für lokale Ressourcen fehlschlägt */}}
    {{ $featured = resources.GetRemote $url | default (resources.Get $url | default "") }}
  {{ end }}

  {{/* Handhabung des Standard-Hintergrunds der Website */}}
  {{ if not $featured }}
   {{ with .Site.Params.defaultBackgroundImage }}
    {{ if or (strings.HasPrefix . "http:") (strings.HasPrefix . "https:") }}
     {{ $featured = resources.GetRemote . | default "" }} {{/* Standardmäßig "" verwenden, wenn GetRemote fehlschlägt */}}
    {{ else }}
     {{ $featured = resources.Get . | default "" }} {{/* Standardmäßig "" verwenden, wenn Get fehlschlägt */}}
    {{ end }}
   {{ end }}
  {{ end }}

  {{/* Die $featured-Ressource verarbeiten, WENN sie gefunden wurde (sollte jetzt ein Ressourcenobjekt sein) */}}
  {{ with $featured }}
    {{/* Prüfen, ob es sich um ein SVG handelt oder die Optimierung deaktiviert ist - Original verwenden */}}
    {{ if or $disableImageOptimization (strings.HasSuffix .RelPermalink ".svg") }}
      {{ $bgImageUrl = .RelPermalink }}
    {{ else }}
      {{/* Größenänderung auf die lokale/abgerufene Bildressource anwenden */}}
      {{ with .Resize (print ($.Site.Params.backgroundImageWidth | default "1200") "x") }}
        {{ $bgImageUrl = .RelPermalink }} {{/* Geänderten relativen Permalink verwenden */}}
      {{ end }}
    {{ end }}
  {{ end }} {{/* Ende with $featured (Ressourcenverarbeitung) */}}

{{ end }} {{/* Ende else (keine externe URL) */}}


{{/* --- Nun die ermittelte $bgImageUrl verwenden, um den Hero-Hintergrund zu rendern --- */}}

{{ $isParentList := eq (.Scratch.Get "scope") "list" }}
{{ $shouldBlur := $.Params.layoutBackgroundBlur | default (or
        (and ($.Site.Params.article.layoutBackgroundBlur | default true) (not $isParentList))
        (and ($.Site.Params.list.layoutBackgroundBlur | default true) ($isParentList))
    ) }}
{{ $shouldAddHeaderSpace := $.Params.layoutBackgroundHeaderSpace | default (or
        (and ($.Site.Params.article.layoutBackgroundHeaderSpace | default true) (not $isParentList))
        (and ($.Site.Params.list.layoutBackgroundHeaderSpace | default true) ($isParentList))
    ) }}

{{/* Die Hintergrund-Div-Struktur nur rendern, WENN wir eine Bild-URL gefunden haben */}}
{{ with $bgImageUrl }}
  {{ $finalUrl := . | absURL }} {{/* Die absolute URL speichern */}}

  {{ if $shouldAddHeaderSpace | default true}}
  <div id="hero" class="h-[150px] md:h-[200px]"></div>
  {{ end }}

  <div class="fixed inset-x-0 top-0 h-[800px] single_hero_background nozoom"
       style="background-image:url({{ $finalUrl }});">

    <div class="absolute inset-0 bg-gradient-to-t from-neutral dark:from-neutral-800 to-transparent mix-blend-normal"></div>
    <div class="absolute inset-0 opacity-60 bg-gradient-to-t from-neutral dark:from-neutral-800 to-neutral-100 dark:to-neutral-800 mix-blend-normal"></div>
  </div>

  {{ if $shouldBlur | default false }}
  <div id="background-blur" class="fixed opacity-0 inset-x-0 top-0 h-full single_hero_background nozoom backdrop-blur-2xl"></div>
  <script>
      window.addEventListener('scroll', function (e) {
          var scroll = window.pageYOffset || document.documentElement.scrollTop || document.body.scrollTop || 0;
          var background_blur = document.getElementById('background-blur');
          background_blur.style.opacity = (scroll / 300)
      });
  </script>
  {{ end }}

{{ end }} {{/* Ende with $bgImageUrl */}}

4. Überlegungen zu anderen Blowfish-Hero-Stilen
#

Das Blowfish-Theme enthält weitere Hero-Stile in seinem layouts/partials/hero/-Verzeichnis, wie zum Beispiel:

  • basic.html
  • big.html
  • thumbAndBackground.html

Wenn Sie diese Hero-Stile verwenden und die Unterstützung für externe R2-Bilder aktivieren möchten, müssen Sie ein ähnliches Änderungsmuster anwenden: Kopieren Sie das entsprechende Partial in das Layout-Verzeichnis Ihres Projekts und aktualisieren Sie es, um nach .Params.featuredImageExternal zu suchen, bevor versucht wird, lokale Bildressourcen zu finden.

Wichtige Überlegungen zur R2-Bildintegration
#

Bei der Integration von Cloudflare R2-Bildern in Ihre Hugo-Website sollten Sie die folgenden Punkte beachten:

  • Hugo-Bildverarbeitung: Hugos leistungsstarke Bildverarbeitungsfunktionen (wie .Resize, .Fit, WebP-Konvertierung) funktionieren nur mit lokalen Ressourcen. Bilder, auf die über Ihre neuen externen Parameter verwiesen wird, werden unverändert verwendet. Sie müssen sicherstellen, dass die Bilder auf R2 bereits für ihre vorgesehene Anzeige optimiert und in der richtigen Größe vorhanden sind.

  • Testen: Testen Sie Beiträge mit und ohne die externen Bildparameter gründlich, um sicherzustellen, dass beide Szenarien korrekt funktionieren.

Durch die Implementierung dieser R2-Integrationsstrategien können Sie die Leistung, Verwaltbarkeit und Skalierbarkeit Ihrer Hugo-Website erheblich verbessern, Ihr Git-Repository schlank halten und die Leistungsfähigkeit des globalen Netzwerks von Cloudflare für die Bildauslieferung nutzen.

Verwandte Artikel

Eine Hugo-Website auf Vercel bereitstellen: Wichtige Fallstricke
·855 Wörter·5 min
Webentwicklung Hugo Vercel Deployment