Unggah file
URL unggah terpresign, konfirmasi, dan sketsa client untuk /api/storage.
Sebelum mengunggah, selesaikan Setup. Unggahan memakai URL terpresign agar file tidak lewat body fungsi serverless Anda.
Alur
sequenceDiagram
participant Client
participant Api as ApiStorage
participant R2 as S3OrR2
Client->>Api: GET /api/storage/upload
Api->>Api: Create File PENDING
Api-->>Client: url plus fileId
Client->>R2: PUT file to presigned url
Client->>Api: POST /api/storage/confirm
Api->>Api: File READY- Client terautentikasi memanggil
GET /api/storage/uploaddengan nama file, content type, dan size opsional. - API membuat Prisma
FileberstatusPENDINGdiusers/{userId}/{uuid}.{ext}dan mengembalikan URL PUT terpresign plusfileId. - Client
PUTbyte file ke URL itu denganContent-Typeyang cocok. - Client memanggil
POST /api/storage/confirmdengan{ fileId }agar baris menjadiREADY.
Route
| Method / path | Auth | Rate limit | Input |
|---|---|---|---|
GET /api/storage/upload | Wajib | WRITE | Query: filename, contentType, size opsional |
POST /api/storage/confirm | Wajib | WRITE | JSON: { fileId } |
Validasi ada di packages/validations/src/storage/storage.ts:
- Tipe diizinkan:
image/jpeg,image/png,image/gif,image/webp,application/pdf,text/plain - Ukuran maks: 10 MB (saat
sizedikirim)
Router: packages/api/src/modules/storage/router.ts. Service: packages/api/src/modules/storage/service.ts.
Sketsa client
async function uploadFile(file: File) {
const params = new URLSearchParams({
filename: file.name,
contentType: file.type,
size: String(file.size),
});
const prepare = await fetch(`/api/storage/upload?${params}`, {
credentials: "include",
});
if (!prepare.ok) throw new Error("Failed to get upload URL");
const { url, fileId } = (await prepare.json()) as {
url: string;
fileId: string;
};
const put = await fetch(url, {
method: "PUT",
body: file,
headers: { "Content-Type": file.type },
});
if (!put.ok) throw new Error("Failed to upload file");
const confirm = await fetch("/api/storage/confirm", {
method: "POST",
credentials: "include",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({ fileId }),
});
if (!confirm.ok) throw new Error("Failed to confirm upload");
return confirm.json();
}Pakai file input atau dropzone pilihan Anda, lalu panggil uploadFile. Cookie sesi harus dikirim (credentials: "include") agar Better Auth mengautentikasi request.
Memperketat aturan
Ubah tipe yang diizinkan atau batas 10 MB di packages/validations/src/storage/storage.ts. Kepemilikan path sudah ditegakkan dengan menghasilkan users/{userId}/... di server — client tidak memilih object key.
Berikutnya
Pelajari cara mengakses file tersimpan dengan URL ter-sign atau publik.