banner
AcoFork

AcoFork

LOVETOLOVE

Cloudflare R2の当ネットワークドライブ!Workers+R2のサーバーレスデプロイ!

警告:R2 の無料容量は 10G で、Workers の無料アクセス制限は 1 日 100 万回です。簡単にトラフィックを不正に増やされる可能性があるため、バインドされたドメインに対してレート制限などの制限手段を設定してください。#

R2 ストレージバケットの作成#

  1. 教えません、簡単なヤピ。詳細はR2 ストレージバケットの作成を参照してください。

R2 と Workers を接続する#

  1. 教えません、簡単なヤピ。詳細はWorkers を作成し、R2 に接続を参照してください。
  2. Workers のコードを以下のように変更し、公開するファイルを別のディレクトリに保存します。ここではguestです。コードには HTML が埋め込まれているので、自分で変更できます:
export default {
  async fetch(request, env, ctx) {
    const bucket = env.MY_BUCKET;
    const rootDirectory = 'guest/';
    const url = new URL(request.url);
    
    // 現在のディレクトリを取得するヘルパー関数
    const getCurrentDirectory = (path) => {
      const cleanPath = path.replace(/^\/+|\/+$/g, '');
      return rootDirectory + cleanPath + (cleanPath ? '/' : '');
    };

    // 親ディレクトリを取得するヘルパー関数
    const getParentDirectory = (path) => {
      const parts = path.split('/').filter(p => p);
      parts.pop();
      return '/' + parts.join('/');
    };

    // 現在のディレクトリ内のファイルとフォルダをリストする
    async function listDirectory(directory) {
      const objects = await bucket.list({ prefix: directory, delimiter: '/' });
      const items = new Set();

      for (const obj of objects.objects) {
        const relativePath = obj.key.slice(directory.length);
        if (relativePath) {
          items.add(relativePath.split('/')[0]);
        }
      }

      for (const prefix of objects.delimitedPrefixes) {
        const relativePath = prefix.slice(directory.length);
        if (relativePath) {
          items.add(relativePath.split('/')[0]);
        }
      }

      return Array.from(items).sort();
    }

    // ディレクトリリストを処理する
    if (!url.pathname.startsWith('/download/')) {
      const currentDirectory = getCurrentDirectory(url.pathname);
      try {
        const items = await listDirectory(currentDirectory);

        let html = `
          <!DOCTYPE html>
          <html lang="en">
          <head>
            <meta charset="UTF-8">
            <meta name="viewport" content="width=device-width, initial-scale=1.0">
            <title>AcoFork R2 Files</title>
            <style>
              body, html {
                margin: 0;
                padding: 0;
                font-family: Arial, sans-serif;
                line-height: 1.6;
                height: 100%;
              }
              body {
                background-image: url('https://hrandom.onani.cn');
                background-size: cover;
                background-position: center;
                background-attachment: fixed;
                display: flex;
                justify-content: center;
                align-items: center;
              }
              .container {
                width: 80%;
                max-width: 800px;
                margin: 40px;
                padding: 30px;
                background-color: rgba(255, 255, 255, 0.5);
                backdrop-filter: blur(10px);
                border-radius: 15px;
                box-shadow: 0 8px 32px rgba(0, 0, 0, 0.1);
              }
              h1 {
                color: #333;
                text-align: center;
                margin-bottom: 30px;
              }
              ul {
                list-style-type: none;
                padding: 0;
                margin: 0;
              }
              li {
                margin-bottom: 10px;
                border: 1px solid rgba(0, 0, 0, 0.1);
                border-radius: 8px;
                overflow: hidden;
                transition: all 0.3s ease;
              }
              li:hover {
                transform: translateY(-3px);
                box-shadow: 0 4px 8px rgba(0, 0, 0, 0.1);
              }
              a {
                display: block;
                padding: 12px 15px;
                text-decoration: none;
                color: #333;
                transition: background-color 0.3s ease;
              }
              a:hover {
                background-color: rgba(255, 255, 255, 0.5);
              }
              .folder::before {
                content: "📁 ";
                color: #FFA500;
              }
              .file::before {
                content: "📄 ";
                color: #008000;
              }
              .parent-link {
                display: inline-block;
                margin-bottom: 20px;
                padding: 8px 15px;
                background-color: rgba(0, 102, 204, 0.8);
                color: white;
                text-decoration: none;
                border-radius: 5px;
                transition: background-color 0.3s ease;
              }
              .parent-link:hover {
                background-color: rgba(0, 82, 163, 0.9);
              }
              @keyframes clickEffect {
                0% { transform: scale(1); }
                50% { transform: scale(0.95); }
                100% { transform: scale(1); }
              }
              .click-effect {
                animation: clickEffect 0.3s;
              }
            </style>
          </head>
          <body>
            <div class="container">
              <h1>私の R2 Files</h1>
        `;
        
        // ルートでない場合は親ディレクトリリンクを追加
        if (currentDirectory !== rootDirectory) {
          const parentDir = getParentDirectory(url.pathname);
          html += `<a href="${parentDir}" class="parent-link">&larr; 親ディレクトリ</a>`;
        }
        
        html += '<ul>';
        for (const item of items) {
          const itemPath = currentDirectory.slice(rootDirectory.length) + item;
          const isFolder = !item.includes('.');
          const className = isFolder ? 'folder' : 'file';

          // ファイルのhrefを外部ダウンロードURLに更新
          const href = isFolder 
            ? `/${encodeURIComponent(itemPath)}` 
            : `https://r2-dl.afo.im/guest/${encodeURIComponent(itemPath)}`;

          html += `<li class="${className}"><a href="${href}" onclick="this.classList.add('click-effect'); setTimeout(() => this.classList.remove('click-effect'), 300);">${item}${isFolder ? '/' : ''}</a></li>`;
        }
        html += `
              </ul>
            </div>
            <script>
              document.addEventListener('DOMContentLoaded', (event) => {
                document.querySelectorAll('a').forEach(link => {
                  link.addEventListener('click', function(e) {
                    if (!this.classList.contains('parent-link')) {
                      e.preventDefault();
                      this.classList.add('click-effect');
                      setTimeout(() => {
                        window.location = this.href;
                      }, 300);
                    }
                  });
                });
              });
            </script>
          </body>
          </html>`;
          
        return new Response(html, {
          headers: { 'Content-Type': 'text/html; charset=utf-8' },
        });
      } catch (error) {
        console.error('オブジェクトのリスト取得中にエラーが発生しました:', error);
        return new Response('オブジェクトのリスト取得中にエラーが発生しました', { status: 500 });
      }
    }

    // その他の無効なリクエストに対するデフォルトの応答
    return new Response('無効なリクエスト', { status: 400 });
  },
};
  1. ファイルのアップロード方法はストレージバケットにファイルを追加するを参照してください。
読み込み中...
文章は、創作者によって署名され、ブロックチェーンに安全に保存されています。