Upgrade to Pro
— share decks privately, control downloads, hide ads and more …
Speaker Deck
Features
Speaker Deck
PRO
Sign in
Sign up for free
Search
Search
RemixとCloudflare Stack におけるFile Upload
Search
Ossamoon
September 25, 2024
Programming
440
1
Share
Embed
Copy iframe code
Copy JS code
Copy link
Start on current slide
RemixとCloudflare Stack におけるFile Upload
Ossamoon
September 25, 2024
More Decks by Ossamoon
See All by Ossamoon
楽譜フォント(SMuFL)をCloudflareで配信する
ossamoon
0
150
SSRアプリケーションにおけるPKCE付き認可コードフロー
ossamoon
0
68
Other Decks in Programming
See All in Programming
自動化したのに回らないテスト運用の壁ーAI時代の品質責任と生産性
mfunaki
0
130
Claude CodeとAgentCore Gatewayを繋ぐ際の認証認可 / Authentication and authorization when connecting Claude Code with AgentCore Gateway
har1101
1
250
What's New in Android 2026
veronikapj
0
250
GDG Korea Android: 2026 I/O Extended ~ What's new in Android development tools
pluu
0
220
改善しないと、タスクが回らない。 “てんこ盛りポジション” を引き継いだ情シスの、入社3ヶ月の業務改善録
krm963
0
260
PHP に部分適用が来るぞ!……ところで何それ?おいしいの? #phpcon / phpcon-2026
shogogg
0
590
プロポーザルを書いてもらう
pvcresin
0
280
AWS CDK を「作」ってみた 〜フルスクラッチで見えた CDK の裏側〜 / aws-cdk-from-scratch
gotok365
3
2.8k
Apache Hive: Toward a Cloud Native Lakehouse
okumin
0
180
ルールを書いて終わらせないハーネスエンジニアリング
yug1224
4
1.9k
言語を使う側から、作る側へ。 自作 Lisp で得た新たな気づき。
andpad
0
150
夏だ!祭りだ!祭りとはドメインモデリングでは?
ryugen04
0
180
Featured
See All Featured
The Art of Delivering Value - GDevCon NA Keynote
reverentgeek
16
2.1k
Typedesign – Prime Four
hannesfritz
42
3.1k
The World Runs on Bad Software
bkeepers
PRO
72
12k
Why Mistakes Are the Best Teachers: Turning Failure into a Pathway for Growth
auna
0
200
Sam Torres - BigQuery for SEOs
techseoconnect
PRO
0
460
Evolving SEO for Evolving Search Engines
ryanjones
0
250
AI Search: Where Are We & What Can We Do About It?
aleyda
0
7.8k
Visualization
eitanlees
152
17k
Stewardship and Sustainability of Urban and Community Forests
pwiseman
0
430
The Straight Up "How To Draw Better" Workshop
denniskardys
239
140k
GitHub's CSS Performance
jonrohan
1033
470k
Effective software design: The role of men in debugging patriarchy in IT @ Voxxed Days AMS
baasie
0
470
Transcript
RemixとCloudflare Stack におけるFile Upload Remix Tokyo Meetup, 2024.09.25
自己紹介 - 齋藤 修(Ossamoon) - Webフロントエンジニア - Reactばかり触っている - フリーランス
- 趣味:音楽 - クラシック音楽が好き
Remixを使ったプロダクトを個人開発中 - クラシック音楽の音源をクラウドにアップロード - 楽譜を表示しながらストリーミング再生 最初の壁として立ちはだかったのが、File Upload
File Uploadに求めること - 認証・認可 - ユーザーを認証したい - アップロード上限を超えていないかをチェックしたい - データベースの更新
- アップロードした音源に関する情報をデータベースに保存したい - アップロードに失敗した場合はデータベースの更新も失敗して欲しい( Atomicity: 原子性) - Cloudflareの制約を満たす - 特にCloudflare Workersは実行時間やメモリの制約が厳しいため、考慮する必要がある
RemixにおけるFile Upload
RemixのFile Uploadに関するAPI - unstable_parseMultipartFormData - unstable_createMemoryUploadHandler - unstable_createFileUploadHandler - Node.jsでのみ動作
- unstable_composeUploadHandlers - APIリファレンスにも載っていない内部的な API すべてmultipart/form-dataを使う想定のAPI 今回は この2つを利用 ← ←
multipart/form-dataとは - HTTPに定義されている仕様 - formに入力したテキストと ファイルのバイナリを 同時に送信できる POST /upload HTTP/1.1
Host: example.com Content-Type: multipart/form-data; boundary=----- Content-Length: 1024 ----- Content-Disposition: form-data; name="field1" value1 ----- Content-Disposition: form-data; name="field2" value2 ----- Content-Disposition: form-data; name="file"; filename="example.jpg" Content-Type: image/jpeg (...ファイルのバイナリデータ...) -----
APIの使い方(action) const uploadHandler = unstable_createMemoryUploadHandler({ maxPartSize: 5 * 1024 *
1024, // これを超えるサイズのファイルは扱えない }); const formData = await unstable_parseMultipartFormData(request, uploadHandler); const title = formData.get("title") as string; const audioFile = formData.get("audiofile") as File; 1. unstable_createMemoryUploadHandler で uploadHandler を作成 2. unstable_parseMultipartFormData で formDataを作成 3. formDataから必要なデータを取得
export async function action({ request, context }: ActionFunctionArgs) { //
認証 const user = await authenticator.isAuthenticated(request); // FormDataを取得 const uploadHandler = unstable_createMemoryUploadHandler({ maxPartSize: 5 * 1024 * 1024, // 5MB }); const formData = await unstable_parseMultipartFormData( request, uploadHandler ); const title = String(formData.get("title")); const audioFile = formData.get("audiofile") as File; // R2にファイルをアップロード const { R2 } = context.cloudflare.env; const object_key = crypto.randomUUID(); const upload_info = await R2.put(object_key, await audioFile.arrayBuffer(), { httpMetadata: { contentType: audioFile.type, }, }); // TrackをDBに追加 const track = await db.track.create(object_key, title, user.id); redirect("tracks"); } 認証とDB更新 も含めた コードの全体像
APIの使い方(component) 1. <Form>のencTypeにmultipart/form-dataを指定 2. あとは普通のformと同様に書く <Form method="post" encType="multipart/form-data"> <label htmlFor="title">タイトル</label>
<input id="title" type="text" name="title" /> <label htmlFor="audiofile">音源ファイル</label> <input id="audiofile" accept="audio/*" type="file" name="audiofile" /> <button type="submit">送信</button> </Form>
multipart/form-dataのメリット・デメリット 👍 実装がシンプル 👍 認証・認可をaction内で処理できる 👍 DB更新も同じaction内で記述することができ る 👎 Workerの実行時間・使用メモリがファイルサ
イズに大きく依存する 👎 Multipart uploadやprogressの表示といった ユーザー体験の最適化が実現しにくい FileUploadのもう一つの実装方法である 署名付きURLも検討したい
署名付きURL
署名付きURLとは - 特定の操作(今回の場合はファイルのアップロード)を認可する署名を追加した URL - Cloudflare R2はS3互換であるため、署名付きURLを用いることができる - Workerで署名付きURLを発行し、クライアントからR2に直接ファイルをアップロード する
- 有効期限を適切に定める必要がある
署名付きURLの流れ 1. クライアントがWorkerに署名付き URLをリクエスト 2. Workerが認証 3. Workerがデータベースに新しいレ コードを作成 4.
Workerが署名付きURLを返す 5. クライアントが署名付きURLを用 いてR2にファイルをアップロード 6. クライアントがWorkerにアップ ロード完了を伝える 7. Workerがデータベースに追加し たレコードを更新 8. (定期実行) ファイルアップロード に失敗した分のデータを削除
署名付きURLのメリット・デメリット 👍 Workerの実行時間・使用メモリがファイル サイズに依存しない 👍 Multipart uploadやprogressの表示といった ユーザー体験の最適化が実現しやすい 👎 有効期限内の署名付き
URLを知れば誰でも アップロードができるので、認証・認可が完 全ではない 👎 Atomicityを保つために実装が複雑になって しまう
さらなるユーザー体験の向上へ - Multipart upload - ファイルをいくつかの partに分割してアップロードする - 並列化による最適化や、ネットワーク切断からの回復に貢献する -
大容量(だいたい100MB以上)のファイルアップロードによく使われる - Progressの表示 - ファイルの何%がアップロードできたかを表示したい - fetchのbodyにReadbleStreamを指定することで、経過を観察することができる - 上記機能はChromeでのみ利用可能 - 他のブラウザもサポートしたい場合、 XMLHttpRequestやaxiosを利用する必要がある これらはRemixの<Form>と組み合わせるのが難しいため、 署名付きURLと組み合わせて使う方がメリットを享受しやすい
まとめ - ファイルサイズが小さい場合は、multipart/form-dataを活用したい - なによりシンプルに書けるのがメリット - ファイルサイズが大きい場合は、署名付きURLを活用したい - ユーザー体験を追求したい場合は、署名付きURLを活用したい