Cloudflare WorkersとR2を用いて画像URL短縮システムを作成する

imgurのようなシステムを自前で構築できないかと思い、Cloudflare WorkersとR2を用いて画像投稿システムを構築したのでそれの紹介をします。

どのようなシステムか?

https://i.smdr.io にapplication/octet-streamでファイルを送信すると、画像URLが返却されるシステムです。
画像ファイルはCloudflare R2に保存され、https://i.smdr.io/slug にアクセスすると3600秒のCacheつきで画像ファイルが返却されます。

画像をアップロードする

やっていることは単純で、トークン認証しつつMath.random().toString(32).substring(6)でslugを生成し、URLを返却しているだけです。

async function postImage(request: Request, env: Env): Promise<Response> {
    if (request.headers.get("X-API-TOKEN") !== env.API_TOKEN) return new Response("Unauthorized", {status: 401})
    const url = new URL(request.url);

    const name = url.pathname === "/" ? Math.random().toString(32).substring(6) : url.pathname.replaceAll("/", "")

    await env.IMAGES.put(name, request.body, {
        httpMetadata: {
            contentType: "image/png"
        }
    })

    return new Response(`https://i.smdr.io/${name}`)
}

画像を取得する

こちらも単純に画像をR2から取得・Cacheしつつ返却しているだけです。


async function getImage(request: Request, env: Env, context: ExecutionContext) {
    const url = new URL(request.url);

    const cacheKey = new Request(url.toString(), request);
    const cache = caches.default;

    let response = await cache.match(cacheKey);

    if (!response) {
        response = await getImageFromBucket(url.pathname.replaceAll("/", ""), env)

        response = new Response(response.body, response)

        response.headers.append('Cache-Control', 's-maxage=120');

        context.waitUntil(cache.put(cacheKey, response.clone()))
    }

    return response
}


async function getImageFromBucket(name: string, env: Env): Promise<Response> {
    const object = await env.IMAGES.get(name);

    if (!object || !object.body) {
        return new Response(`Object Not Found: ${name}`, {status: 404})
    }

    const headers = new Headers();
    object.writeHttpMetadata(headers);
    headers.set('etag', object.httpEtag);

    return new Response(object.body, {
        headers,
    });
}

etagについて知らないので、etag周りは公式サンプルコピペです(:3」∠)_

これでcurlなどからアップロードすることができるようになりました。

curl https://i.smdr.io/ -X POST -H "X-API-TOKEN:..." --data-binary @face.jpeg --header "Content-Type:application/octet-stream"

ただ、これだとアップロードがかなり面倒なので、Appleのショートカットを利用して「imgurにアップロード」と同じようにできるようにします。

ショートカットからアップロードする

1q348ko

(この画像もi.smdr.ioを利用しています)

共有シートなどから画像もしくはファイルを受け取り、i.smdr.ioにリクエストし、その内容をコピーしています。こうすることでiPhone・Mac両対応をしています。

作ってみて

Cloudflare R2は初めて触ったのですが、かなり使いやすい印象でした。get,putしか利用していませんがメタデータだけ取得するなどできさまざまな用途に使えるなと思いました。