1397 字
7 分钟
博客图床测试

封面图片 来源

这篇博客用于测试基于本博客专用的图床系统,该图床基于 Cloudflare Workers 和 Cloudflare R2 存储桶搭建。

以下查看使用 Markdown 直接插入图片的显示效果。

图片

理论上来说,该图片仅在本博客下可以正常显示,使用新标签页打开此图片的来源地址时会禁止访问

注意,从本博客点击图片来源地址仍可以访问,例如点击博客开头的封面图片 来源 或在图片上右击选择新标签页打开,此时仍可以访问图片。如果直接新建空白标签页并粘贴图片地址,访问将被禁止

搭建方法来源于 CloudflareR2快速开始使用(白嫖)指南Cloudflare R2对象存储当图床测试

基于 Cloudflare 的搭建方法#

首先创建 R2 存储桶。

然后创建 Cloudflare Workers 项目,代码如下:

addEventListener("fetch", event => {
    event.respondWith(handleRequest(event.request))
})

async function handleRequest(request) {
    const url = new URL(request.url)
    const objectName = url.pathname.slice(1)

    console.log(`${request.method} object ${objectName}: ${request.url}`)

    if (request.method === 'GET' || request.method === 'HEAD') {
        if (objectName === '') {
            if (request.method == 'HEAD') {
                return new Response(undefined, { status: 400 })
            }

            const options = {
                prefix: url.searchParams.get('prefix') ?? undefined,
                delimiter: url.searchParams.get('delimiter') ?? undefined,
                cursor: url.searchParams.get('cursor') ?? undefined,
                include: ['customMetadata', 'httpMetadata'],
            }
            console.log(JSON.stringify(options))

            const listing = await R2.list(options)
            return new Response(JSON.stringify(listing), {
                headers: {
                    'content-type': 'application/json; charset=UTF-8',
                }
            })
        }

        if (request.method === 'GET') {
            const range = parseRange(request.headers.get('range'))
            const object = await R2.get(objectName, {
                range,
                onlyIf: request.headers,
            })

            if (object === null) {
                return objectNotFound(objectName)
            }

            const headers = new Headers()
            //object.writeHttpMetadata(headers)
            headers.set('etag', object.httpEtag)
            const status = object.body ? (range ? 206 : 200) : 304
            return new Response(object.body, {
                headers,
                status
            })
        }

        const object = await R2.head(objectName, {
            onlyIf: request.headers,
        })

        if (object === null) {
            return objectNotFound(objectName)
        }

        const headers = new Headers()
        //object.writeHttpMetadata(headers)
        headers.set('etag', object.httpEtag)
        return new Response(null, {
            headers,
        })
    }
    if (request.method === 'PUT' || request.method == 'POST') {
        const object = await R2.put(objectName, request.body, {
            httpMetadata: request.headers,
        })
        return new Response(null, {
            headers: {
                'etag': object.httpEtag,
            }
        })
    }
    if (request.method === 'DELETE') {
        await R2.delete(url.pathname.slice(1))
        return new Response()
    }

    return new Response(`Unsupported method`, {
        status: 400
    })
}

function parseRange(encoded) {
    if (encoded === null) {
        return
    }

    const parts = encoded.split("bytes=")[1]?.split("-") ?? []
    if (parts.length !== 2) {
        throw new Error('Not supported to skip specifying the beginning/ending byte at this time')
    }

    return {
        offset: Number(parts[0]),
        length: Number(parts[1]) + 1 - Number(parts[0]),
    }
}

function objectNotFound(objectName) {
    return new Response(`<html><body>R2 object "<b>${objectName}</b>" not found</body></html>`, {
        status: 404,
        headers: {
            'content-type': 'text/html; charset=UTF-8'
        }
    })
}

回到新建的 Workers 项目首页,来到 设置 - 变量 - R2 存储桶绑定添加绑定 后变量名称填写 R2 ,R2 存储桶选择刚才创建的图床存储桶,然后 部署

使用方法#

来到 Cloudflare R2 存储桶首页,选择图床所在的存储桶,直接上传图片即可。需要访问图片时,如果你的图床 Workers 项目自定义域为 img.example.com ,图片文件名为 img.png ,那么访问地址为 https://img.example.com/img.png

安全性措施#

Cloudflare 提供的 R2 存储桶的免费容量为 10 GB 每月,免费的 A 类操作次数为 100 万次每月,免费的 B 类操作次数为 1000 万次每月。正常自己使用是用不完的。被刷了那就乖乖掏钱吧

配置 Cloudflare 缓存#

新方法#

推荐使用新方法,新方法不会占用免费的3个页面规则,两种方法选一种配置即可。

来到图床所在 域名 - 缓存 - Cache Rules创建规则

规则名称 随便填。

当传入请求匹配时…

字段运算符
主机名等于img.example.com

img.example.com 即为图床的地址。

则…

缓存资格:符合缓存条件

添加 边缘 TTL:忽略缓存控制标头,使用此 TTL => 6 个月

添加 浏览器 TTL:忽略缓存控制标头,使用此 TTL => 1 个月

然后 部署

传统方法#

不推荐,此方法会占用免费的3个页面规则。

来到图床所在 域名 - 规则 - 页面规则创建页面规则

URLimg.example.com/*

img.example.com 即为图床的地址,/* 是指该地址下所有内容。

点击 + 添加设置

选取设置选择
缓存级别缓存所有内容
浏览器缓存 TTL1天 最高都可以到1年
边缘缓存 TTL1个月

然后 保存和部署页面规则

现在访问图片时,访问的都是 Cloudflare CDN 服务器上的内容,不会占用 R2 存储桶的操作次数。

浏览器缓存 TTL 是指浏览器第一次访问这个地址后,就会把地址对应的文件缓存到本地。

接下来在 TTL 时间内,浏览器再次访问这个地址时,都是直接调用本地缓存的文件。

边缘缓存 TTL 是指你第一次访问这个地址后,为你提供这个地址资源的 CDN 节点会缓存这个地址对应的文件。

接下来在 TTL 时间内,你再次通过这个节点访问这个地址时,这个节点会直接调用缓存的文件。

TTL 就是缓存有效期,过期后就要重新缓存。

防盗链#

防盗链就是防止别的网站直接抓取我的网站,然后显示在它的网站上,就是防止我的图床承担其他网站的访问流量然后免费次数被刷爆了掏钱

来到图床所在 域名 - 安全性 - WAF创建规则

规则名称 随便填。

字段运算符
引用方不包含blog.example.com
And
主机名等于img.example.com

其中 blog.example.com 即为博客地址,img.example.com 即为图床地址。

然后采取措施:阻止

此时只有在博客 blog.example.com 内访问图床 img.example.com 才可以成功。如果不是从博客 blog.example.com 内访问图床 img.example.com ,访问请求将被阻止。

这样对图床地址就有了一个基本的保护。

博客图床测试
https://blog.wely.fun/posts/博客图床测试/
作者
Wely
发布于
2024-05-02
许可协议
CC BY-NC-SA 4.0