Upgrade to Pro
— share decks privately, control downloads, hide ads and more …
Speaker Deck
Sign up for free
Menu
Search
Features
All features
Private URLs
Password Protection
Custom URLS
Scheduled publishing
Remove Branding
Restrict embedding
Deck Collections
Notes
Explore
Featured decks
Featured speakers
Programming
Technology
Storyboards
Pricing
Search
Sign in
Sign up for free
RemixとCloudflare Stack におけるFile Upload
Search
Ossamoon
September 25, 2024
Programming
450
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
170
SSRアプリケーションにおけるPKCE付き認可コードフロー
ossamoon
0
73
Other Decks in Programming
See All in Programming
『寄り添うラジオ』をAIで作る 体験価値から逆算した、会話しないUXと品質設計
theoriatec2024
3
130
新人はどこまで自力でやり、どこからAIに頼るべきか/エンジニア育成に向き合う_先輩たちの悩みと知見共有会
toppan_digital_dev
1
580
Intent as Code
shoppingjaws
2
480
AIエージェント時代のコードレビューを設計する
nogu66
6
2.6k
業務時間外もAIに働いてもらう話
colorful12
3
10k
20260828_品質と開発生産性を両立させる、AI時代のE2Eテストの考え方
magicpod
0
170
デプロイ直後のレイテンシスパイクを調べたら、 Railsの仕様にたどり着いた
nhsykym
0
110
初心者DevRelとして参加者だった私が、DevRel Talks!#2に登壇するまでにしてきたこと
sokohirai
0
340
[GoCon2026] When Goroutines Are Not Enough: Runtime Locality in High-Throughput Go
takehaya
4
660
{ Android | Kotlin } Gradle Plugin in 2026
ryunen344
1
290
Webプラットフォームで議論されているセキュリティ課題 / Security issues being discussed on Web Platforms
petamoriken
0
260
市販E-Readerを乗っ取れ 〜Embedded Swiftで電子ペーパーガジェットを制御する〜
trickart
0
130
Featured
See All Featured
Reality Check: Gamification 10 Years Later
codingconduct
0
2.3k
jQuery: Nuts, Bolts and Bling
dougneiner
66
8.6k
Efficient Content Optimization with Google Search Console & Apps Script
katarinadahlin
PRO
1
840
Designing for humans not robots
tammielis
254
26k
Mind Mapping
helmedeiros
1
350
The Invisible Side of Design
smashingmag
301
52k
Exploring the Power of Turbo Streams & Action Cable | RailsConf2023
kevinliebholz
37
6.6k
The Myth of the Modular Monolith - Day 2 Keynote - Rails World 2024
eileencodes
28
3.6k
KATA
mclloyd
PRO
35
15k
Facilitating Awesome Meetings
lara
57
7.1k
Producing Creativity
orderedlist
PRO
348
41k
The Success of Rails: Ensuring Growth for the Next 100 Years
eileencodes
47
8.3k
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を活用したい