Cloudflare Workers + R2部署Hugo解决方案

Cloudflare Workers + R2部署Hugo解决方案

Cloudflare Workers + R2部署Hugo网站,突破Workers文件数上限解决方案

下面我将详细解释如何使用 Cloudflare R2 部署 Hugo 网站,包括完整的配置文件和代码示例:

完整解决方案步骤(含详细配置)

1. 创建 R2 存储桶

  1. 登录 Cloudflare 控制台
  2. 进入 R2 → 创建桶
  3. 输入桶名称(如 my-hugo-site
  4. 选择 智能分层(默认即可)

2. 上传 Hugo 文件到 R2

wrangler学习文档:https://developers.cloudflare.com/workers/wrangler/

# 安装 R2 命令行工具(如已安装可跳过)
npm install -g wrangler

# 登录 Cloudflare
npx wrangler login

# 递归上传整个 public 目录
npx wrangler r2 object put my-hugo-site/ --directory ./public --recursive

使用 wrangler r2 object put 命令、R2 API、S3 兼容工具(如 s3cmd, awscli)或支持 R2 的图形化工具(如 Cyberduck)将 Hugo 生成的整个 public/ 目录上传到 R2 桶。

#  通过运行以下命令创建存储桶:
npx wrangler r2 bucket create <YOUR_BUCKET_NAME>
# 检查您的存储桶是否已创建
npx wrangler r2 bucket list

运行该命令后,您将看到所有存储桶名称,包括您刚刚创建的存储桶名称。

# 升级wrangler
 npm install -g wrangler@4.22.0
 
# 上传整个目录
npx wrangler r2 object put "docs-maiyi-xyz/" --file ./public/*
# 单个文件上传示例
npx wrangler r2 object put "docs-maiyi-xyz/index.html" --file ./public/index.html
备选方案:使用 S3 兼容 API

如果 Wrangler 仍然有问题,可以使用 AWS CLI 替代:

# 安装 AWS CLI
pip install awscli

# 配置凭据
aws configure set aws_access_key_id YOUR_R2_ACCESS_KEY
aws configure set aws_secret_access_key YOUR_R2_SECRET_KEY
aws configure set default.region auto

# 上传文件
aws s3 sync ./public s3://docs-maiyi-xyz --endpoint-url=https://<YOUR_ACCOUNT_ID>.r2.cloudflarestorage.com

3. 配置 wrangler.toml

在 Hugo 项目根目录创建/修改 wrangler.toml

name = "my-hugo-worker"
main = "worker.js"  # Worker 入口文件
compatibility_date = "2024-03-01"

[[r2_buckets]]
binding = "ASSETS"  # Worker 中使用的变量名,有效的JavaScript变量名
bucket_name = "my-hugo-site"  # R2 桶名称

4. 创建 Worker 脚本 (worker.js)

// 获取文件扩展名对应的 MIME 类型
const getContentType = (filename) => {
  const extension = filename.split('.').pop().toLowerCase();
  const types = {
    html: 'text/html',
    css: 'text/css',
    js: 'application/javascript',
    json: 'application/json',
    png: 'image/png',
    jpg: 'image/jpeg',
    jpeg: 'image/jpeg',
    gif: 'image/gif',
    svg: 'image/svg+xml',
    ico: 'image/x-icon',
    txt: 'text/plain',
    webp: 'image/webp',
    webm: 'video/webm',
    mp4: 'video/mp4',
    pdf: 'application/pdf',
    woff: 'font/woff',
    woff2: 'font/woff2'
  };
  return types[extension] || 'application/octet-stream';
};

// 主请求处理函数
export default {
  async fetch(request, env) {
    const url = new URL(request.url);
    let pathname = url.pathname;

    // 处理根路径请求
    if (pathname === '/') {
      pathname = '/index.html';
    }

    // 移除路径开头的斜杠
    let objectName = pathname.startsWith('/') 
      ? pathname.substring(1) 
      : pathname;

    try {
      // 尝试获取请求的文件
      const object = await env.ASSETS.get(objectName);

      // 文件不存在时的处理
      if (!object) {
        // 尝试查找 index.html(用于处理目录请求)
        if (!objectName.endsWith('/index.html')) {
          const altPath = objectName.endsWith('/') 
            ? `${objectName}index.html` 
            : `${objectName}/index.html`;
          
          const altObject = await env.ASSETS.get(altPath);
          if (altObject) {
            return buildResponse(altObject, altPath);
          }
        }
        
        // 返回 404 页面(如果存在)
        const notFound = await env.ASSETS.get('404.html');
        return notFound
          ? buildResponse(notFound, '404.html')
          : new Response('Not Found', { status: 404 });
      }

      // 返回找到的文件
      return buildResponse(object, objectName);

    } catch (err) {
      return new Response('Internal Server Error', { status: 500 });
    }
  }
};

// 构建响应对象
function buildResponse(object, filename) {
  const headers = new Headers();
  
  // 设置内容类型
  headers.set('Content-Type', getContentType(filename));
  
  // 设置缓存控制(1天)
  headers.set('Cache-Control', 'public, max-age=86400');
  
  // 设置 ETag 用于缓存验证
  headers.set('ETag', object.httpEtag);
  
  return new Response(object.body, { headers });
}

5. 部署 Worker

# 在 Hugo 项目根目录执行
wrangler deploy

6. 自动化部署脚本 (可选)

创建 deploy.sh 实现一键部署:

#!/bin/bash

# 生成 Hugo 网站
hugo --minify

# 上传到 R2
wrangler r2 object put my-hugo-site/ --directory ./public --recursive

# 部署 Worker
wrangler deploy

运行权限:chmod +x deploy.sh

关键点详解

  1. R2 绑定机制

    • binding = "ASSETS" 在 TOML 中定义
    • 在 Worker 中通过 env.ASSETS 访问
    • 使用 .get(key) 方法获取文件对象
  2. 智能路径处理

    • 自动将 / 重定向到 /index.html
    • 自动处理目录请求(如 /about//about/index.html
    • 自定义 404 页面支持
  3. 性能优化

    • 自动设置正确的 Content-Type
    • 添加 Cache-Control 头(1天缓存)
    • ETag 支持缓存验证
  4. 错误处理

    • 文件不存在时尝试查找 index.html
    • 自定义 404 页面
    • 500 错误捕获

常见问题解决

Q: 如何更新网站内容? A: 重新运行 hugo 生成网站后,再次执行上传和部署:

./deploy.sh

Q: 某些文件类型返回错误 Content-Type? A: 在 getContentType 函数中添加对应的 MIME 类型,例如:

// 添加新类型
avif: 'image/avif',
wasm: 'application/wasm',

Q: 需要自定义重定向规则? A: 在 Worker 中添加路由处理逻辑:

// 在获取文件前添加重定向规则
if (pathname === '/old-page') {
  return Response.redirect('https://yoursite.com/new-page', 301);
}

Q: 如何添加自定义域名? A: 在 Cloudflare 控制台:

  1. Workers & Pages → 你的 Worker → 触发器
  2. 在 “Custom Domains” 中添加域名
  3. 在 DNS 设置中添加 CNAME 记录指向 Worker

优势总结

  1. 突破文件限制:R2 无文件数量限制
  2. 成本更低:免费额度包含 10GB 存储 + 100 万次/日读取
  3. 无缝集成:与 Cloudflare CDN 深度集成
  4. 部署简单:单个 Worker 脚本处理所有请求
  5. 高性能:边缘网络全球加速

提示:对于大型网站(>1GB),建议使用 rclone 替代 wrangler 上传:

rclone copy --progress ./public r2:my-hugo-site