Closed10

Remix+CloudflareでWebサイトを作る 11(R2に画像アップロード・wrangler.tomlはCommitしてOK・Workers Proに課金・バンドルサイズ最適化)

saneatsusaneatsu

【2024-02-28】R2に画像をアップロードする

準備

1. R2のバケット作成

2. 「R2 APIトークンの管理」からトークンを作成

3. チェック

トークンが有効であることを確認。

$ curl -X GET "https://api.cloudflare.com/client/v4/user/tokens/verify" \
     -H "Authorization: Bearer YOUR_TOKEN" \
     -H "Content-Type:application/json"
{"result":{"id":"xxxxxxxxx","status":"active"},"success":true,"errors":[],"messages":[{"code":10000,"message":"This API Token is valid and active","type":null}]}%

DB_NAMEYOUR_FILE_NAME という名前で FILE_PATH/FILE_NAME をアップロードできることを確認。

$ npx wrangler r2 object put DB_NAME/YOUR_FILE_NAME.jpg --file=FILE_PATH/FILE_NAME.png
 ⛅️ wrangler 3.28.3 (update available 3.29.0)
-------------------------------------------------------
Creating object "YOUR_FILE_NAME.jpg" in bucket "DB_NAME".
Upload complete.

4. コードを修正してローカルで画像アップロード

wrangler.toml
[[r2_buckets]]
binding = "BUCKET"
bucket_name = "your-bucket-name"
remix.env.d.ts
interface Env {
  DB: D1Database;
  BUCKET: R2Bucket; // 追加
}
const schema = z.object({
  file: z
    .instanceof(File, { message: "ファイルを選択してください" })
    .transform((file) => file)
    .refine((file) => file.size < 500 * 1000, {
      message: "ファイルサイズは最大5MBです",
    })
    .refine(
      (file) => ["image/jpeg", "image/jpg", "image/png"].includes(file.type),
      {
        message: ".jpeg .jpgもしくは.pngのみ可能です",
      }
    ),
});

// ref: https://dev.classmethod.jp/articles/remix-upload-cloudflare-r2/
export const action = async ({ request, context }: ActionFunctionArgs) => {
    const formData = await request.clone().formData();
    const submission = parseWithZod(formData, { schema });
    if (submission.status !== "success") {
      throw new Error('error')
    }

    // R2に画像をアップロードしてURLを取得
    const env = context.env as Env;
    const uploadHandler = unstable_createMemoryUploadHandler({
      maxPartSize: 1024 * 1024 * 10,
    });
    
    const form = await unstable_parseMultipartFormData(request, uploadHandler);
    const file = form.get("file");        
    const response = await env.BUCKET.put("hoge.png", file);
    console.log(response);
    
    return json({ success: true });
};

function Layout() {
  return (
    // ref: https://conform.guide/file-upload
    <Form
        method="POST"
        id={form.id}
        onSubmit={form.onSubmit}
        encType="multipart/form-data"
      >
  )
}

.wrangler/state/v3/r2/DB_NAME/blobs/* が増えることでファイルがアップロードされていることが確認できる。
action()console.log は以下。

HeadResult {
  range: undefined,
  customMetadata: {},
  httpMetadata: {},
  uploaded: 2024-02-29T12:13:52.333Z,
  checksums: Checksums {
    sha512: undefined,
    sha384: undefined,
    sha256: undefined,
    sha1: undefined,
    md5: ArrayBuffer {
      [Uint8Contents]: <ce c1 4d 6d de f5 54 8c c8 f1 b4 f8 cf 9e a6 6c>,
      byteLength: 16
    }
  },
  httpEtag: '"cec14d6ddef5548cc8f1b4f8cf9ea66c"',
  etag: 'cec14d6ddef5548cc8f1b4f8cf9ea66c',
  size: 448438,
  version: '2593c3321aa0021c2b80904789c37e32',
  key: 'hoge.png'
}

DBのカラム名にimage_urlっていう感じにしたど、R2使う場合はカラム名をr2_key とかにしてkey を値として入れるということで良いのかな。

R2の料金

Cloudflare R2 | エグレス料金ゼロのオブジェクトストレージ | Cloudflare

無料で月に1000万も読み取れるのか。ありがてぇありがてぇ...。

参考

saneatsusaneatsu

【2024-02-29】wrangler.toml のファイルに色々と直書きしてCommitしたくないんですけれども

つまり「wrangler.tomlに秘匿情報を書き込んだままCommitしたくないから、開発環境の場合なら .dev.vars、Staging/Production環境なら"Workers & Pages"で定義できるCloudflareの環境変数をwrangler.toml で読み込みたい」ということ。

方法がなかなか見つからない。

ところでこのときwrangler.tomlはignoreさせなくていいいのかということについて気になってたがIssueを発見。
https://github.com/cloudflare/wrangler-legacy/issues/209#issuecomment-541654484

it's completely Fine to have your zone_id and account_id public, the Global API key and associated email address should be kept secret.
everything in a wrangler.toml is committable to publicly accessible version control :)

グローバルAPIキーやメールアドレス以外ならOKらしい。

4年半前のIssueだけど今でも大丈夫なんですかね...。
ほんとにぃ〜??🤔
こえ〜

saneatsusaneatsu

【2024-02-29】compatibility_dateをpackage.jsonからwrangler.tomlに移す

compatibility_dateとは

https://developers.cloudflare.com/workers/configuration/compatibility-dates/

指定した日付までの互換性を保証するまでに使用される。
つまり compatibility_date = "2024-02-01" の場合、その日付以前の機能に対する互換性が保証され、その日以降の機能はサポートされない可能性がある。

公式のドキュメントでは特に「V8 JavaScriptエンジン」などの言及はされていなかったがChatGPTに聞くと以下のように言われた。

このオプションは、Workerが互換性のあるJavaScriptエコシステムとどの程度の互換性を持つかを指定します
具体的には、compatibility_dateはJavaScriptの機能や構文に関する互換性を指定するために使用されます。Cloudflare Workersは、V8 JavaScriptエンジンをベースにしており、通常は最新のJavaScript機能をサポートしています。しかし、一部の機能はまだ実装されていない場合があります。compatibility_dateを設定することで、特定の日付までのJavaScriptの互換性を保証することができます。

修正範囲

デフォルトではpackage.jsonに書かれていた。

package.json
-  "start": "wrangler pages dev --compatibility-date=2023-06-21 ./public",
+  "start": "wrangler pages dev ./public",
wrangler.toml
+ compatibility_date = "2024-02-01"
saneatsusaneatsu

【2024-03-01】ローカルでR2にアップロードした画像を取得する方法がわからん

背景

3つ前のここでローカルのR2(= .wrangler/state/v3/r2/DB_NAME/*) にアップロードしたけど、<img> でどうやってパスを指定して表示するのかわからん。

Production環境のR2はカスタムドメインを設定しているから普通にURLを指定すると表示はできるんだけど、ローカルの場合どうやれば良いのだろうか??

// Staging/Productionの場合はこうかけば良さそう
<img
  src="https://images.domain.com/icon.png"
  alt=""
  style={{ width: "200px", height: "auto" }}
/>

方法(調べ中)

公式ドキュメントを見る限りローカルだけで再現する方法もありそうだし、ローカルからリモートに接続して動作確認する方法もありそう。

1. wrangler dev でローカルで再現

https://developers.cloudflare.com/workers/observability/local-development-and-testing/

In addition to testing Workers locally with wrangler dev, the use of Miniflare allows you to test other Developer Platform products locally, such as R2, KV, D1, and Durable Objects.

R2もローカルで利用できるような書き方がされているけどどうなんだろう

2. wrangler dev --remote でローカルからプレビュー環境にアクセスする

https://developers.cloudflare.com/workers/observability/local-development-and-testing/#develop-locally-using-remote-resources-and-bindings

npx wrangler dev --remote という感じで--remoteオプションを付けるとローカルからリモートのリソースにアクセスできるっぽい。

エラー: Missing entry-point

とりあえず wrangler dev を動くか確認。

$ npx wrangler dev
 ⛅️ wrangler 3.28.3 (update available 3.30.1)
-------------------------------------------------------

✘ [ERROR] Missing entry-point: The entry-point should be specified via the command line (e.g. `wrangler dev path/to/script`) or the `main` config field.

Commands - Wrangler · Cloudflare Workers docs を見ると以下のように書かれてある。

The path to an entry point for your Worker. Only required if your wrangler.toml does not include a main key (for example, main = "index.js").

remix.config.js に書いてあるパスを引数に与えればOK。

remix.config.js
export default {
  serverBuildPath: "functions/[[path]].js",
};

エラー: MiniflareCoreError

# zshを使用していると[[ ]] はメタ文字として解釈されるためシングルクォーテーションで囲んでいる
$ npx wrangler dev 'functions/[[path]].js'[ERROR] workerd/server/workerd-api.c++:685: error: wrapped binding module can't be resolved (internal modules only); moduleName = cloudflare-internal:d1-api


✘ [ERROR] MiniflareCoreError [ERR_RUNTIME_FAILURE]: The Workers runtime failed to start. There is likely additional logging output above.

なかなか治せない...。

2つ前のScrap でRemixをv2.7にしたときはこのコマンドは動いていた。

v2.7を断念したのはCloudflare Workersのバンドルサイズが1MBでその制限を超えたからなんだけど、Service Bindingを使えばどうにかなるらしいのでそっち方向で頑張ろうかな。
そしてさっきv2.7になったと思ったらもうv2.8になってるのね。

saneatsusaneatsu

【2024-03-02】Workersのバンドルサイズが1MB超えてる問題を解決するために課金(wrangler dev でエラーでてる続き)

v2.7にしてどれくらいサイズが増えた?

v2.6からv2.7のときに1MB超えているというエラーがでたけどそもそもサイズはどれくらいだったからエラーになったんだ?と思ったので調べてみる

$ npx wrangler deploy 'functions/[[path]].js' --outdir bundled/ --dry-run

--dry-run: exiting now.
Total Upload: 1420.32 KiB / gzip: 346.75 KiB

ん?これは1.4MBってことなのかな?
けど Limits · Cloudflare Workers docs を見ると以下のように書いてある。

You can assess the size of your Worker bundle after compression by performing a dry-run with wrangler and reviewing the final compressed (gzip) size output by wrangler:

gzipの方は1MB超えていないのでは...?

じゃあ、v2.6の時のサイズいくつなんだ?と思ってみてみたら以下。

Total Upload: 1384.59 KiB / gzip: 335.48 KiB

ん???
Total Upload の方だったら既に超えてたことになるからやっぱgzipの方だな...。

課金

そもそも設定が何かおかしかったりする説かと思って色々試してみたけどだめだった。
課金しよう。
自分の時給を考えたらここに何時間もかけるより月5ドルかけるほうがいいよ!!どうせいろいろ機能増えて1MB超えるしな、と言い聞かせてみる。

課金したら1MB→10MBになることはこっちに書いた。
この課金導線の確認ページでも書いておいてほしいけどな?

解決された...💸

あれ、AMEXの利用履歴とCloudflareからの購入メール、どちらにも請求金額が3.10ドルときている。
なんで5ドルじゃないんだ??
円安割ですか??

saneatsusaneatsu

【2023-03-02】バンドルサイズの再計算

バンドルサイズの計算ミスってた。
多分昔のビルドに対してサイズ計算していた。

Remixのv2.7にアプデするPRを整理しながらfunctions/[[path]].tsに実行してみたら1.6MBだった。
1つ前の投稿では、v2.6系で使っていたfunctions/[[path]].js に対してサイズ計算していた。

$ npx wrangler deploy 'functions/[[path]].ts' --outdir bundled/ --dry-run
 ⛅️ wrangler 3.29.0 (update available 3.30.1)

--dry-run: exiting now.
Total Upload: 7920.32 KiB / gzip: 1671.66 KiB

minifyオプションを付けると1.4MB

$ npx wrangler deploy 'functions/[[path]].ts' --outdir bundled/ --dry-run --minify
Total Upload: 5264.50 KiB / gzip: 1417.86 KiB

文字も赤色になり忠告が出る。

そうなるとやはりgzipが345KB→1670KBに4.5倍以上に跳ね上がっている理由が気になる。

saneatsusaneatsu

【2023-03-02】バンドルサイズの最適化

ここにまさにCloudflare Workersの1MBの制限に対して最適化していくことが書かれていた。

https://speakerdeck.com/mizchi/server-side-javascript-notamenobandoruzui-shi-hua?slide=11

エッジで動かすならデカくないほうがいいよと。

このScrapでも書いたけどサイズ意識するの大事だなぁ。

一旦課金で回避するけど徐々にバンドルサイズ小さくするようにしていこう。

MelodyclueMelodyclue

なかなか最新のバージョンでこういうことやっている方いなくて要約このスクラップ見つけたんですが、

npx wrangler dev 'functions/[[path]].ts'

これがエラーになってしまうのって何か解決できましたでしょうか?

このスクラップは2024/03/02にクローズされました