banner
AcoFork

AcoFork

LOVETOLOVE

Cloudflare R2 as a cloud storage! Serverless deployment with Workers + R2!

WARNING: The free capacity of R2 is 10G, and the free access limit for Workers is 1 million times per day. It is easy to get traffic brushed, so please set rate limits and other restrictions for your bound domain to avoid becoming a millionaire loser overnight.#

Create an R2 Bucket#

  1. No more teaching, it's simple and chic. See Create R2 Bucket

Connect R2 with Workers#

  1. No more teaching, it's simple and chic. See Create Workers, Connect R2
  2. Change the Workers code to the following, setting a directory to store the files to be made public, here it is guest. The code contains embedded HTML, which can be modified as needed:
export default {
  async fetch(request, env, ctx) {
    const bucket = env.MY_BUCKET;
    const rootDirectory = 'guest/';
    const url = new URL(request.url);
    
    // Helper function to get the current directory
    const getCurrentDirectory = (path) => {
      const cleanPath = path.replace(/^\/+|\/+$/g, '');
      return rootDirectory + cleanPath + (cleanPath ? '/' : '');
    };

    // Helper function to get parent directory
    const getParentDirectory = (path) => {
      const parts = path.split('/').filter(p => p);
      parts.pop();
      return '/' + parts.join('/');
    };

    // List files and folders in the current directory
    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();
    }

    // Handle directory listing
    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>My R2 Files</h1>
        `;
        
        // Add parent directory link if not in root
        if (currentDirectory !== rootDirectory) {
          const parentDir = getParentDirectory(url.pathname);
          html += `<a href="${parentDir}" class="parent-link">&larr; Parent Directory</a>`;
        }
        
        html += '<ul>';
        for (const item of items) {
          const itemPath = currentDirectory.slice(rootDirectory.length) + item;
          const isFolder = !item.includes('.');
          const className = isFolder ? 'folder' : 'file';

          // Update href for files to use the external download 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 listing objects:', error);
        return new Response('Error listing objects', { status: 500 });
      }
    }

    // Default response for any other invalid requests
    return new Response('Invalid request', { status: 400 });
  },
};
  1. How to upload files see Add Files to Your Bucket
Loading...
Ownership of this post data is guaranteed by blockchain and smart contracts to the creator alone.