0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

CloudFlare Workersを使ってR2にアクセスする

Last updated at Posted at 2023-12-14
YUMEMI New Grad Advent Calendar 2023

前回の記事ではGradleを使ってR2にファイルを保存しました。
またPublicに公開する設定をしました。

しかしR2の公開設定だけではリポジトリを利用する人に利用可能なパッケージの一覧を見せることができません。
maven-metadata.xmlの参照はできるのでバージョンはわかりますが、ブラウザからアクセスするとファイルがダウンロードされてしまい気軽に確認できません。
(GradleからアップロードするとContent-Typeが application/octet-stream になるため)

そこで今回はWorkersに簡単なアプリケーションを作成し、一覧として見られるようにしていきます。
またファイルの配布もできるようにしてWorkersのURLとMaven RepositoryのURLを一致させられるようにすることを目指します。
R2のBucketと同じアカウントにWorkersを作成します。

基本的にはドキュメントに沿ってコードを書くだけの記事です。
Workersの作成とドメインの設定はTerraformで行います。
Workersの設定とコードのアップロードはWranglerで行います。

コードは以下のリポジトリで公開しています。

参考

Workers用のコードを作成

Honoを使っていきます。

HonoのTutorialの中にR2を扱う方法が書かれているのでこちらも参考にしていきます。

R2のBucketのBindingの設定

まず、R2のBucketのBindingの設定をしていきます。この操作によってWorkersのコードからR2へ簡単にアクセスすることができます。
HonoでWorkers用にファイルを生成するとwrangler.tomlが生成されているので、こちらに以下を追記します。
もともとコメントアウトされて同じ内容が書かれています。

[[r2_buckets]]
binding = "MAVEN_BUCKET"
bucket_name = "koma-maven"
preview_bucket_name = "koma-maven"

これでR2の koma-maven という名前のBucketに MAVEN_BUCKET の変数名でアクセスできるようになります。
preview_bucket_name の設定は動作確認をする時に使うBucketの名前です。今回は本番と同じBucketを設定しますが、PUTなどの動作を入れる際に開発用に用意したBucketを使ってください。
Bindingの設定については以下のドキュメントに詳しい説明が書かれています。
地理的な制約がある場合の設定も書かれているので該当する場合にはその設定も必要です。今回は特に設定しません。

Workersのコードでの使用

先ほどBindingしたR2をWorkersで扱うためのコードの記述をしていきます。
JSXを使うためファイルの拡張子をtsからtsxに変更しています。

typescript index.tsx
type Bindings = {
    MAVEN_BUCKET: R2Bucket;
};

const app = new Hono<{Bindings: Bindings}>();

Bindings のタイプで先ほど設定したBindingの名前(r2_bucketsの binding)を指定します。
これでHonoのcontextから参照できるようになります。

一覧を表示するために以下のようにコードを記述しました。 / にアクセスした際にファイルの一覧を表示するようにしています。

typescript index.tsx
app.get("/", async (c) => {
    const bucket = c.env.MAVEN_BUCKET;
    const items = await bucket.list();
    return c.html(
        <Page title="Files">
            <ol>
                {items.objects.map((o) => {
                    const name = o.key;
                    const path = `/${name}`;
                    return (
                        <li>
                            <a href={path}>{name}</a>
                        </li>
                    );
                })}
            </ol>
        </Page>
    );
});

const Page: FC<{title: string}> = (props) => {
    return html`<!doctype html>
        <html lang="en">
            <head>
                <title>${props.title}</title>
            </head>
            <body>
                ${props.children}
            </body>
        </html>`;
};

c.env.MAVEN_BUCKET の記述でR2のBucketにアクセスしています。
利用できる関数については以下に記載があります。今回は一覧を取得したいため list を使っています。

このコードで以下のように一覧表示ができます。

ディレクトリ階層ごとに表示したい場合にはlistの引数にprefixを渡したりパスの解析をする必要があります。
またlistの引数で delimiter を指定することで返り値に delimitedPrefixes
が含まれ、次の階層の情報が得られる
ようです。

個々のファイルのパスにアクセスした際にファイルの中身が取得できるように、以下のコードを記述しました。

app.get("/:prefix{.+$}", async (c) => {
    const bucket = c.env.MAVEN_BUCKET;
    const {prefix} = c.req.param();
    const bucketObject = await bucket.get(prefix);
    if (bucketObject !== null) {
        c.header("etag", '"' + bucketObject.etag + '"');
        c.header("Content-Type", "application/octet-stream");
        return c.stream(async (stream) => {
            await stream.pipe(bucketObject.body);
        });
    } else {
        return c.notFound();
    }
});

get でファイルを取得し、存在すればその中身を、存在しなければ404を返すようにしています。
ドキュメント
に記載がありますが、bucketObject.body でStreamが得られるのでそれを返すようにしています。
今回はContent-Typeを固定で application/octet-stream にしていますが、拡張子やファイルの中身から適切なMIME
typesを取得して設定するとブラウザでファイルの中身が確認しやすくなります。

コードの全体はこちらです。

Workersの動作確認

書いたコードが実際に動作するか確認していきます。

ローカル

以下のコマンドでローカル環境に立ち上げることができます。
ただしR2の中身を確認することはできません。空のBucketとして振る舞います。

npx wrangler dev src/index.tsx

上のコマンドで実行している途中に l を入力すると実際のWorkers上で起動するモードに変わります。(
下記のリモートと同じ挙動になります)

リモート

実際にR2を使って動作確認するには以下のコマンドで立ち上げます。

npx wrangler dev src/index.tsx --remote

このコマンドでは実際にWorkersにコードを送って、ローカルのポートとの紐付けを行っているようです。
wrangler.tomlの preview_bucket_name のBucketを使用できます。

Workersの作成

以下のコードでリソースの作成を行いました。Terraformで作成まで行い、管理はWranglerで行うようにしています。
(Terraformで管理しているのは関連するリソースをdestroyしやすくするためです)

data "http" "script" {
  url = "https://gist.githubusercontent.com/jRiest/7893cf10c550057ce1ff53f270683e1c/raw/3ac7f45302f4f6274703c564864a684e9097bce2/party_parrot_worker.js"
}

resource "cloudflare_worker_script" "worker" {
  account_id = var.cloudflare_account
  # https://blog.cloudflare.com/deploy-workers-using-terraform/
  content    = data.http.script.response_body
  name       = var.worker_name
  lifecycle {
    ignore_changes = [
      content,
      r2_bucket_binding,
    ]
  }
}

resource "cloudflare_worker_domain" "worker_domain" {
  account_id = var.cloudflare_account
  hostname   = "test.${var.bucket_name}.${var.domain}" # test.koma-maven.example.com
  service    = cloudflare_worker_script.worker.name
  zone_id    = data.cloudflare_zone.zone.id
}

cloudflare_worker_scriptは初回から有効なコードを指定する必要があるため、Terraformの例
に載っていたコードのURLを最初のコードとして使うようにしています。
cloudflare_worker_domainでWorkersを紐づけるドメインを指定しています。CloudFlareでDNSの設定がされているドメインのみが使用できます。
Custom Domainを使うかRouteを使うかはこちら
を参照してください。今回はR2にアクセスするだけなのでCustom Domainにしています。
ドメインを持っていない場合はRouteにして、workers.dev 配下のサブドメインを使うことになります。今回の範囲では動作が変わることはありません。

この後WranglerでコードのデプロイとBindingの設定を行うのでそれに関連する部分はignore_changesにしています。
他にどのような設定をTerraformでできるかはドキュメントに記載があります。

Workersのデプロイ

以下のコマンドでデプロイします

wrangler deploy --minify src/index.tsx

上記のWorkersの作成の手順でコードをデプロイしたので、Terraformでアップロードしたコードを上書きしていいかと聞かれます。
yをおして許可します。

その後適切に設定されていれば、cloudflare_worker_domainのhostnameで指定したドメインでアクセスできます。

各ファイルのリンクをクリックすればファイルがダウンロードできます。

Gradleでの設定

前回R2で用意されているBucketの公開設定で作成したURLをRepositoryに設定していました。

repositories {
    maven {
        name = "R2"
        url = uri("https://koma-maven.example.com/")
    }
}

dependencies {
    implementation("jp.co.yumemi.koma:lib:1.1-SNAPSHOT")
}

それを今回Workersを配置したURLに変更します。

- url = uri("https://koma-maven.example.com/")
+ url = uri("https://test.koma-maven.example.com/")

これでWorkersからデータを取得できます。
このURLにアクセスすればファイルの一覧が見られるので他のファイルを確認するのも簡単です。

まとめ

WorkersからR2を扱ってファイル一覧を確認できるようにしました。TypeScriptのコードをもっと工夫すればGradleでのコード例を示したりできると思います。
R2を扱うのにBindingの設定をすこし書くだけなので非常に簡単です。R2への認証も気にしなくていいのでサッと扱うには楽です。

読み書きの使用量については注意してください。
今回コードで使用したlistはClass Aの ListObjects の処理にあたります。
Class Aは無料枠が月100万リクエストなので、それを超えないようお気をつけください。
Custom Domainでデプロイしている場合にはCacheを使用
できるので、そちらでAPIのアクセス回数を減らすことができます。

0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?