Cloudflare R2 图片 URL 显示问题:详细说明和修复

问题描述

我们最近注意到论坛上的图片显示存在一个问题,具体表现为:

  • 图片缩略图未正确显示。
  • 点击图片可以正确显示全尺寸图片。
  • 浏览器开发者工具显示错误的图片 URL。

经过排查,根本原因是图片 URL 中的域名不正确:

  • 正确 URL: https://store.starorigin.cc/optimized/1X/[imageID].jpeg
  • 错误 URL: https://info.7a4081a2d83d3f43fe6b1be1c926fd1c.r2.cloudflarestorage.com/optimized/1X/[imageID].jpeg

系统正在使用原始 R2 存储桶的错误域名,而不是我们配置的 CDN 域名。

技术分析

这是 Discourse 在处理存储在 Cloudflare R2 中的图片时的一个已知问题。在某些情况下,即使配置了 s3_cdn_url,Discourse 在生成优化图片(如缩略图)时仍可能使用原始存储 URL 而不是 CDN URL。

这可能与以下因素有关:

  • Discourse 版本
  • S3 兼容存储的配置
  • URL 在 OptimizedImage 表中的存储方式

解决方案

最简单有效的解决方案是使用 Discourse 主题组件进行客户端修复。这不需要任何数据库操作或服务器配置更改。它只需要添加一个简短的 JavaScript 代码片段,该代码片段会自动在浏览器中将错误的 URL 替换为正确的 URL。

主题组件代码

import { apiInitializer } from "discourse/lib/api";

export default apiInitializer("0.11.1", (api) => {
  // 修复已加载的图片
  function fixImageUrls() {
    const badDomain = "info.7a4081a2d83d3f43fe6b1be1c926fd1c.r2.cloudflarestorage.com";
    const goodDomain = "store.starorigin.cc";

    // 修复常规图片
    document.querySelectorAll(`img[src*="${badDomain}"]`).forEach(img => {
      img.src = img.src.replace(badDomain, goodDomain);
    });

    // 修复延迟加载的图片
    document.querySelectorAll(`img[data-src*="${badDomain}"]`).forEach(img => {
      img.setAttribute('data-src', img.getAttribute('data-src').replace(badDomain, goodDomain));
    });

    // 修复背景图片
    document.querySelectorAll('[style*="background"]').forEach(el => {
      if (el.style.backgroundImage && el.style.backgroundImage.includes(badDomain)) {
        el.style.backgroundImage = el.style.backgroundImage.replace(badDomain, goodDomain);
      }
    });

    // 修复各种其他潜在的属性
    ['srcset', 'data-large-src', 'data-small-src', 'data-download-href'].forEach(attr => {
      document.querySelectorAll(`[${attr}*="${badDomain}"]`).forEach(el => {
        el.setAttribute(attr, el.getAttribute(attr).replace(badDomain, goodDomain));
      });
    });
  }

  // 修复编辑器中的图片
  api.decorateCooked($elem => {
    fixImageUrls();
  }, { id: 'fix-r2-image-urls' });

  // 初始加载后修复
  api.onPageChange(() => {
    fixImageUrls();
  });

  // 处理动态加载的内容
  const observer = new MutationObserver(mutations => {
    fixImageUrls();
  });

  // 在 DOM 加载后开始观察
  if (document.readyState === "loading") {
    document.addEventListener('DOMContentLoaded', () => {
      fixImageUrls();
      startObserver();
    });
  } else {
    fixImageUrls();
    startObserver();
  }

  function startObserver() {
    observer.observe(document.body, {
      childList: true,
      subtree: true,
      attributes: true,
      attributeFilter: ['src', 'data-src', 'srcset', 'style']
    });
  }
});

代码工作原理

此代码执行以下操作:

  1. 全面检测: 查找包含错误域名的所有图片 URL。
  2. 多元素处理: 处理各种图片元素和属性(img 标签、延迟加载图片、背景图片等)。
  3. 动态监控: 使用 MutationObserver 监控页面更改,确保动态加载的内容也得到修复。
  4. Discourse 集成: 与 Discourse API 集成,以处理各种特殊场景。

安装步骤

  1. 登录您的 Discourse 管理员帐户。
  2. 转到 Admin > Customize > Theme Components
  3. 点击 New 按钮。
  4. 选择 Create new component 选项。
  5. 将其命名为“Fix R2 Image URLs”(或您喜欢的任何名称)。
  6. 在“Javascript”选项卡中,粘贴上面的代码。
  7. 点击 Create 按钮。
  8. 点击 Enable 按钮,然后选择要应用的主题(通常是“Default”)。

验证

安装后:

  1. 刷新论坛页面。
  2. 查看包含图片的帖子。
  3. 确认缩略图已正确显示。
  4. 使用浏览器的开发者工具检查图片请求是否都指向 CDN 域名。

客户端解决方案是最简单、最快捷、风险最低的方法,尤其是在服务器访问受限的情况下。

结论

这个简单的主题组件有效地解决了 Discourse 与 Cloudflare R2 存储集成时的图片 URL 问题,无需进行服务器更改或复杂的配置。虽然它是在客户端修复问题,而不是解决根本原因,但它易于实现,可立即看到效果,并且是一个理想的解决方案。

如果您的 Discourse 站点也遇到类似问题,请随时尝试此解决方案。

4 个赞

此问题是否仅影响聊天?我正在尝试了解范围。

感谢您在此提供的非常有用的报告!

1 个赞

是的,此 bug 只会影响聊天。我的 S3 服务提供商是 Cloudflare R2。当我在聊天界面发送图片时,图片无法使用默认的 CDN 链接设置,导致图片加载失败。

1 个赞

似乎仍然是类似 Upload images in chat can't be show normally when use s3 CDN 的情况,并且曾尝试修复但后来被撤销了。