ファイルを置く
置く:アップロードのしくみ
初心者が必ず踏む落とし穴つき
倉庫に荷物を入れます。入れ方は2つあって、手で1枚だけ置く方法と、フォームから受け取って自動で置く方法です。順番にやります。そして、この章には初心者が100%踏む落とし穴が1つあります。
この章でできるようになること
- 手元の画像を1枚、R2に置いて確認する
- 「本物のR2に入ったのか、練習用の場所に入ったのか」を見分ける
- フォームから送られた画像を、Workerが受け取ってR2に保存する
まず、手で1枚置いてみる
プロジェクトのフォルダに、適当な画像を1枚置いてください。名前は sample.png にします。そのうえで、こう頼みます。
フォルダにある sample.png を、my-photos バケットに images/sample.png というキーで保存してください。 必ず本物のR2(リモート)に保存してください。ローカルの模擬ストレージではありません。保存できたら、リモートから取得し直して、ちゃんと入っているか確認してから教えてください。
この依頼文には、わざと同じことを2回書いてあります。理由は次のとおりです。
落とし穴:黙っていると、本物には入りません
R2にファイルを入れる操作は、何も指定しないと、あなたのパソコンの中の「練習用の模擬ストレージ」に保存されます。画面には「保存しました」と出ます。エラーも出ません。それなのに、Cloudflareの管理画面を見ると空っぽです。
実際、画面にはこう表示されます。
Resource location: local
Use --remote if you want to access the remote instance.
local と書いてあったら、本物ではありません。この1行を見落とすと、「保存されているはずなのに表示されない」という迷宮に入ります。僕はここで時間を溶かしました。
本物に入れると、同じ場所の表示が変わります。
Resource location: remote
ローカルの模擬ストレージ
練習用の、パソコンの中だけにある偽のR2です。本物を汚さずに試せる、便利な仕組みではあります。ただ、初めての人には「保存できたのに見えない」という混乱の原因にしかなりません。この教科書では、依頼文に必ず「本物のR2に」と書くことで避けます。
うまくいったか確認する
- 実行結果に
Resource location: remoteと出ている - Cloudflareの管理画面でR2を開き、
my-photosの中にimages/sample.pngが見える - 管理画面でファイルサイズが0バイトになっていない
本命:フォームから受け取って保存する
毎回コマンドで入れるのでは、自分しか使えません。ブラウザから選んだ画像が、そのままR2に入るようにします。
横にスクロールできます
画像アップロードの仕組みを作ってください。 ・トップページに、画像を1枚選んで送信できるフォームを置く ・Workerが /upload でそれを受け取り、BUCKETバインディングを使ってR2に保存する ・キーは images/ で始め、重複しないようにランダムな文字列を混ぜる ・画像の種類(Content-Type)も一緒に保存する ・画像以外のファイルや、5MBを超えるファイルは断って、日本語のメッセージを返す ・保存できたら、保存したキーをJSONで返す 作り終わったら、それぞれの部分が何をしているかを日本語で説明してください。
できあがるプログラムの中心は、この3行です。
const key = `images/${crypto.randomUUID()}-${file.name}`;
await env.BUCKET.put(key, file.stream(), {
httpMetadata: { contentType: file.type },
});
1行ずつ読むと、こう書いてあります。
- 1行目:重複しない名前を作る。
crypto.randomUUID()は、毎回違うでたらめな文字列を作る道具です - 2行目:
env.BUCKETにput(置く)。第9章で決めた呼び名がここに出てきます - 3行目:ファイルの種類を一緒に覚えさせる。これが次の章で効いてきます
Content-Type(コンテントタイプ)
「これはPNG画像です」「これはPDFです」という、中身の種類を表す札です。R2は中身を見ないので、この札を貼っておかないと、あとで取り出したときにブラウザが画像として表示してくれません。保存するときに一緒に持たせるのが鉄則です。忘れると、次の章で「画像がダウンロードされてしまう」という症状になります。
アップロード前の確認(バリデーション)
送られてきたものが本当に画像か、大きすぎないかを、保存する前に調べることです。誰でも送れる窓口を開ける以上、ここは必ず入れます。無制限に受け取ると、無料枠を一晩で食いつぶされることもあり得ます。
動かして確かめる
アップロードを試したいです。本物のR2につながる形でローカル起動して、開くURLを教えてください。 起動できたら、私がブラウザで画像を送ったあとに、R2の中身を一覧して、本当に増えているかを確認してください。
フォームから画像を送ると、キーが返ってきます。images/ で始まる長い文字列です。R2の一覧にも、それが増えているはずです。
よくあるつまずき
- Resource location: local
- この章の落とし穴です。本物ではなく練習用に保存されています。「本物のR2(リモート)に保存し直してください」と頼んでください。
- TypeError: env.BUCKET is undefined
- 第9章のbindingが効いていません。
wrangler.jsoncにr2_bucketsがあるか、呼び名がBUCKETになっているかを確認してもらってください。 - アップロードは成功するのに、管理画面のファイルが0バイト
- 中身が空のまま保存されています。ファイルの読み取り方に問題があるので、「保存したファイルのサイズが0になっています。原因を調べて直してください」と伝えてください。
- 同じ画像を2回上げたら、1個しか増えない
- 同じキーになっていて、上書きされています。ランダムな文字列がキーに入っているか確認してください。
- 大きな画像を送ると失敗する
- 制限に当たっています。まずは自分で決めた上限(5MBなど)を確認してください。それを超えていないのに失敗する場合は、エラーの文面をそのままClaude Codeに貼って聞くのが最短です。
この章のまとめ
手で1枚置き、フォームからも置けるようにしました。キーは自動で重複しない形にして、種類の札も貼りました。そしてlocal と remote の違いという、この分野で一番の初見殺しを覚えました。
ただ、いま入れた画像は、まだ誰にも見えません。次の章で外に出します。
章末チェックリスト
保存できました。次の第11章で、置いた画像を見せます。