Cloudflare 通过 Workers 访问 R2 中存储的文件并搭建文件服务器

CF 通过 Workers 访问 R2 中存储的文件并搭建文件服务器

先看效果图:
在这里插入图片描述
利用CF中的R2对象存储功能保存文件,再利用CF中的计算(Workers)将WEB界面显示出来

部署Workers中的代码

如下图
在这里插入图片描述
Workers中代码如下:

export default {
	async fetch(request, env) {
	  const url = new URL(request.url);
	  const key = url.pathname.slice(1);
  
	  if (request.method === 'GET') {
		// 处理文件下载
		if (key) {
		  const object = await env.MY_BUCKET.get(key);
  
		  if (object === null) {
			return new Response('File not found', { status: 404 });
		  }
  
		  const headers = new Headers();
		  object.writeHttpMetadata(headers);
		  headers.set('etag', object.httpEtag);
  
		  return new Response(object.body, {
			headers,
		  });
		}
  
		// 显示文件列表为 HTML 表格
		const list = await env.MY_BUCKET.list();
		let fileListHtml = `
		  <html>
			<head>
			  <meta charset="UTF-8">
			  <title>文件列表</title>
			  <style>
				body {
				  font-family: Arial, sans-serif;
				  margin: 20px;
				}
				table {
				  max-width: 900px;
				  width: 100%;
				  border-collapse: collapse;
				  margin: 20px auto;
				}
				th, td {
				  padding: 8px;
				  text-align: left;
				  border: 1px solid #ddd;
				}
				th {
				  background-color: #f2f2f2;
				}
				a {
				  text-decoration: none;
				  color: blue;
				}
				.file {
				  padding-left: 20px;
				}
			  </style>
			</head>
			<body>
			  <h1 style="text-align: center;">文件列表</h1>
			  <table>
				<tr>
				  <th>文件名</th>
				  <th>文件大小 (MB)</th>
				  <th>上传日期</th>
				</tr>`;
  
		// 用来处理文件的函数
		const displayFiles = (files) => {
		  let html = '';
		  files.forEach(obj => {
			const fileSizeMB = (obj.size / (1024 * 1024)).toFixed(2); // 转换为 MB
  
			// 获取文件上传日期
			let uploadDate = obj.httpMetadata?.created ? new Date(obj.httpMetadata.created) : new Date(obj.lastModified);
  
			// 如果日期无效,则使用当前日期
			if (isNaN(uploadDate.getTime())) {
			  uploadDate = new Date();
			}
  
			// 格式化日期为 YYYY/MM/DD
			const formattedDate = uploadDate.toLocaleString('zh-CN', {
			  year: 'numeric',
			  month: '2-digit',
			  day: '2-digit',
			});
  
			// 提取文件名部分,去掉路径
			const fileName = obj.key.split('/').pop();  // 获取文件的基本名称
  
			html += `
			  <tr class="file">
				<td><a href="${obj.key}" download="${fileName}">${fileName}</a></td>
				<td>${fileSizeMB} MB</td>
				<td>${formattedDate}</td>
			  </tr>`;
		  });
		  return html;
		};
  
		// 处理所有文件,过滤掉目录,直接列出所有层级的文件
		const files = list.objects.filter(obj => !obj.key.endsWith('/')); // 获取文件(不包含目录)
		fileListHtml += displayFiles(files);
  
		fileListHtml += `
			  </table>
			</body>
		  </html>`;
  
		return new Response(fileListHtml, {
		  headers: { 'Content-Type': 'text/html; charset=UTF-8' },
		});
	  }
  
	  return new Response('Method not allowed', { status: 405 });
	},
  };
  

创建R2存储桶

CF提供免费10GB空间,流量免费,超出后收费。搭建小型文件服务站正好合适,开通教程参考其它大佬的开通方法,开通时需要绑定银行卡,超出扣款将从此账号扣费。

绑定R2存储桶

在这里插入图片描述
名称为:MY_BUCKET,上述代码中使用的为MY_BUCKET
值为:R2存储桶中的名称(如果已有存储桶绑定时为选择的桶名)

绑定自定义域名

可直接使用此域名访问搭建好的网站
注:自定义域名需要托管到CF管理或者域名在CF账号中才能使用自定义域名
在这里插入图片描述

在上述代码的基础上做了微调,并增加了自动生成Favicon图标的功能

export default {
	async fetch(request, env) {
	  const url = new URL(request.url);
	  const key = url.pathname.slice(1);
  
	  if (request.method === 'GET') {
		// 处理文件下载
		if (key) {
		  const object = await env.MY_BUCKET.get(key);
  
		  if (object === null) {
			return new Response('File not found', { status: 404 });
		  }
  
		  const headers = new Headers();
		  object.writeHttpMetadata(headers);
		  headers.set('etag', object.httpEtag);
  
		  return new Response(object.body, {
			headers,
		  });
		}
  
		// 显示文件列表为 HTML 表格
		const list = await env.MY_BUCKET.list();
		let fileListHtml = `
		  <html>
			<head>
			  <meta charset="UTF-8">
			  <title>文件列表</title>
			  <script>
			    function generateFavicon() {
			      const canvas = document.createElement("canvas");
			      canvas.width = 64;
			      canvas.height = 64;
			      const ctx = canvas.getContext("2d");

			      // 创建渐变色(从上到下)
			      const gradient = ctx.createLinearGradient(0, 0, 0, 64);
			      gradient.addColorStop(0, "#FF5733");  // 上部颜色
			      gradient.addColorStop(1, "#FFC300");  // 下部颜色
			      
			      // 绘制渐变背景
			      ctx.fillStyle = gradient;
			      ctx.fillRect(0, 0, canvas.width, canvas.height);
			      
			      // 设置文字样式
			      ctx.font = "bold 32px Arial";
			      ctx.fillStyle = "#FFFFFF";
			      ctx.textAlign = "center";
			      ctx.textBaseline = "middle";
			      
			      // 在中心绘制 "AU"
			      ctx.fillText("AU", 32, 32);

			      // 转换为 Data URL
			      const faviconURL = canvas.toDataURL("image/png");

			      // 创建并替换 Favicon
			      let link = document.querySelector("link[rel='icon']") || document.createElement("link");
			      link.rel = "icon";
			      link.href = faviconURL;
			      document.head.appendChild(link);
			    }

			    // 页面加载后执行
			    window.onload = generateFavicon;
			  </script>
			  <style>
				body {
				  font-family: Arial, sans-serif;
				  margin: 20px;
				}
				table {
				  max-width: 900px;
				  width: 100%;
				  border-collapse: collapse;
				  margin: 20px auto;
				}
				th, td {
				  padding: 8px;
				  text-align: left;
				  border: 1px solid #ddd;
				}
				th {
				  background-color: #f2f2f2;
				}
				a {
				  text-decoration: none;
				  color: blue;
				}
				.file {
				  padding-left: 20px;
				}
			  </style>
			</head>
			<body>
			  <h1 style="text-align: center;">文件列表</h1>
			  <table>
				<tr>
				  <th>文件名</th>
				  <th>文件大小 (MB)</th>
				  <th>上传日期</th>
				</tr>`;
  
		// 用来处理文件的函数
		const displayFiles = (files) => {
		  let html = '';
		  files.forEach(obj => {
			const fileSizeMB = (obj.size / (1024 * 1024)).toFixed(2); // 转换为 MB
  
			// 获取文件上传日期
			let uploadDate = obj.httpMetadata?.created ? new Date(obj.httpMetadata.created) : new Date(obj.lastModified);
  
			// 如果日期无效,则使用当前日期
			if (isNaN(uploadDate.getTime())) {
			  uploadDate = new Date();
			}
  
			// 格式化日期为 YYYY/MM/DD
			const formattedDate = uploadDate.toLocaleString('zh-CN', {
			  year: 'numeric',
			  month: '2-digit',
			  day: '2-digit',
			});
  
			// 提取文件名部分,去掉路径
			const fileName = obj.key.split('/').pop();  // 获取文件的基本名称
  
			html += `
			  <tr class="file">
				<td><a href="${obj.key}" download="${fileName}">${fileName}</a></td>
				<td>${fileSizeMB} MB</td>
				<td>${formattedDate}</td>
			  </tr>`;
		  });
		  return html;
		};
  
		// 处理所有文件,过滤掉目录,直接列出所有层级的文件
		const files = list.objects.filter(obj => !obj.key.endsWith('/')); // 获取文件(不包含目录)
		fileListHtml += displayFiles(files);
  
		fileListHtml += `
			  </table>
			</body>
		  </html>`;
  
		return new Response(fileListHtml, {
		  headers: { 'Content-Type': 'text/html; charset=UTF-8' },
		});
	  }
  
	  return new Response('Method not allowed', { status: 405 });
	},
};

生成的图标为下图中的,可以自行替换文件标题与图标
在这里插入图片描述

Content-Security-Policy: frame-src 'self' *.mozilla.net *.mozilla.org *.mozilla.com *.mozilla.org www.googletagmanager.com www.google-analytics.com www.youtube-nocookie.com trackertest.org www.surveygizmo.com accounts.firefox.com accounts.firefox.com.cn www.youtube.com; img-src 'self' *.mozilla.net *.mozilla.org *.mozilla.com *.mozilla.org data: mozilla.org www.googletagmanager.com www.google-analytics.com adservice.google.com adservice.google.de adservice.google.dk creativecommons.org cdn-3.convertexperiments.com logs.convertexperiments.com images.ctfassets.net; child-src 'self' *.mozilla.net *.mozilla.org *.mozilla.com *.mozilla.org www.googletagmanager.com www.google-analytics.com www.youtube-nocookie.com trackertest.org www.surveygizmo.com accounts.firefox.com accounts.firefox.com.cn www.youtube.com; style-src 'self' *.mozilla.net *.mozilla.org *.mozilla.com *.mozilla.org 'unsafe-inline' app.convert.com; script-src 'self' *.mozilla.net *.mozilla.org *.mozilla.com *.mozilla.org 'unsafe-inline' 'unsafe-eval' www.googletagmanager.com www.google-analytics.com tagmanager.google.com www.youtube.com s.ytimg.com cdn-3.convertexperiments.com app.convert.com data.track.convertexperiments.com 1003350.track.convertexperiments.com 1003343.track.convertexperiments.com; connect-src 'self' *.mozilla.net *.mozilla.org *.mozilla.com *.mozilla.org www.googletagmanager.com www.google-analytics.com region1.google-analytics.com logs.convertexperiments.com 1003350.metrics.convertexperiments.com 1003343.metrics.convertexperiments.com sentry.prod.mozaws.net o1069899.sentry.io o1069899.ingest.sentry.io https://accounts.firefox.com/ stage.cjms.nonprod.cloudops.mozgcp.net cjms.services.mozilla.com; font-src 'self'; default-src 'self' *.mozilla.net *.mozilla.org *.mozilla.com *.mozilla.org X-Clacks-Overhead: GNU Terry Pratchett Etag: "f2d65cb9e0a72b2e713c5f4e624ec2c1" X-Backend-Server: bedrock-75f4585775-g8pf7.gcp-us-west1 Strict-Transport-Security: max-age=31536000 X-Content-Type-Options: nosniff X-Xss-Protection: 1; mode=block Referrer-Policy: strict-origin-when-cross-origin Via: 1.1 google, 1.1 5d4ed4df24dee2cc4c8f561e8c090690.cloudfront.net (CloudFront) X-Cache: Miss from cloudfront X-Amz-Cf-Pop: SFO5-C1 X-Amz-Cf-Id: Yz8DuOFyPa9l63XgCBJwQlo9VYl2Ch9qG9ccVATJcR6ci3doX7QbHA==表示什么意思
06-03
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值