Skip to content

Хранилища для загрузок

Готовые загрузчики из @bloklabs/presets — чтобы Blok было куда сохранять загруженные файлы без необходимости писать собственный обработчик загрузки. Каждый из них подключается напрямую через опцию конфигурации uploader.

ПресетПовторно размещает файл по URL?Готов к продакшену?
Fetch endpointДаДа
SupabaseНетДа
Presigned URLsНетДа
CloudinaryДаДа
IndexedDBНетНет

Fetch endpoint

Talks to a backend you already have. Every upload goes through your server, which decides where the bytes actually land.

Повторное размещение файла по URL

Yes — your endpoint does the fetch server-side, so the browser never has to reach a third-party URL itself.

Конфигурация

ОпцияТипПо умолчаниюОписание
baseUrlstring(required)Origin of your upload service, e.g. "https://api.myapp.com".
fieldstring"file"Multipart field name the endpoint reads for POST /upload.
headersRecord<string, string> | (() => Promise<Record<string, string>>)undefinedExtra headers on every request. Pass a function to mint a fresh access token per upload.

Настройка хранилища

  • A server that answers POST {baseUrl}/upload (multipart) and POST {baseUrl}/upload-by-url ({ url }) with { url, fileName? } JSON.
  • Where the endpoint stores the file — S3, a disk volume, Supabase — is entirely the endpoint's decision; this preset never sees it.
  • The URL your /upload-by-url endpoint receives is user-supplied — validate it server-side and block internal/private addresses before fetching it, or you have built an SSRF vector.

Пример использования

TypeScript
import { fetchStorage } from '@bloklabs/presets';

new Blok({
  holder: 'editor',
  uploader: fetchStorage({ baseUrl: 'https://api.myapp.com' }),
});

Supabase

Uploads straight to a Supabase Storage bucket using the client you already initialize.

Повторное размещение файла по URL

No — re-hosting a remote URL needs a server-side fetch, and the browser can't make one on Supabase's behalf. uploadByUrl is left undefined so Blok stores the URL verbatim instead of pretending it was re-hosted.

Конфигурация

ОпцияТипПо умолчаниюОписание
bucketstring | ((kind: AssetKind) => string)"blok"Bucket name, or a function of the asset kind for per-kind buckets.
path(file: File, ctx: UploadContext) => stringrandom name, original extension keptObject path within the bucket.

Настройка хранилища

  • Create the bucket in the Supabase dashboard (default name "blok", or whatever `bucket` resolves to).
  • Make the bucket public, or add a Storage policy granting anon SELECT — this preset returns getPublicUrl() directly, so an unreadable object is a broken image.
  • Add a Storage INSERT policy for whichever role the client authenticates as.

Пример использования

TypeScript
import { createClient } from '@supabase/supabase-js';
import { supabaseStorage } from '@bloklabs/presets';

const supabase = createClient(SUPABASE_URL, SUPABASE_ANON_KEY);
new Blok({ holder: 'editor', uploader: supabaseStorage(supabase, { bucket: 'blok' }) });

Presigned URLs

For S3 and S3-compatible storage (R2, MinIO, GCS). Your backend mints a short-lived signed URL; the browser PUTs the file straight to it.

Повторное размещение файла по URL

No — re-hosting a remote URL needs a server-side fetch, which is outside what a presigned PUT URL can do. uploadByUrl is left undefined so Blok stores the URL verbatim instead.

Конфигурация

ОпцияТипПо умолчаниюОписание
sign(request: SignRequest) => Promise<SignedTarget>(required)Called with { fileName, mimeType, size, kind }; must return { uploadUrl, publicUrl, headers? } from your backend.

Настройка хранилища

  • A backend endpoint that mints a presigned PUT URL (e.g. S3 PutObjectCommand) and returns it as SignedTarget.
  • A CORS rule on the bucket allowing PUT (and the headers this preset sends, notably Content-Type) from your app's origin — this is what actually blocks people, since the browser PUTs directly to the bucket.

Пример использования

TypeScript
import { presignedStorage } from '@bloklabs/presets';

new Blok({
  holder: 'editor',
  uploader: presignedStorage({ sign: (request) => api.sign(request) }),
});

Cloudinary

Uploads directly to Cloudinary using an unsigned upload preset — no backend required.

Повторное размещение файла по URL

Yes — Cloudinary fetches the remote URL itself once the browser hands it over, so re-hosting works without any server of the consumer's.

Конфигурация

ОпцияТипПо умолчаниюОписание
cloudNamestring(required)Your Cloudinary cloud name.
uploadPresetstring(required)Must be an UNSIGNED upload preset — a signed one would need a server.
folderstringundefinedOptional folder to upload into.

Настройка хранилища

  • A Cloudinary account and its cloud name.
  • An upload preset with signing mode set to Unsigned (Settings → Upload → Upload presets) — a signed preset silently fails from the browser since there is no server to sign the request.
  • Audio assets ride Cloudinary's video pipeline by Cloudinary's own design; this preset maps kinds onto resource types accordingly, no setup needed for that part.

Пример использования

TypeScript
import { cloudinaryStorage } from '@bloklabs/presets';

new Blok({
  holder: 'editor',
  uploader: cloudinaryStorage({ cloudName: 'my-cloud', uploadPreset: 'blok-unsigned' }),
});

IndexedDB

Stores uploaded files as blobs in the browser's own IndexedDB — nothing leaves the device.

Повторное размещение файла по URL

No — there is nothing to re-host to; a remote URL just gets stored as-is. IndexedDB only has an uploadByFile.

Не для продакшена

Demo and prototyping only. The bytes live in one visitor's browser: they're gone on another device, in another browser, or the moment the user clears site data. It exists because Blok's built-in fallback (a blob: URL) doesn't even survive a page reload, which makes demos look broken before you've configured any real storage.

Конфигурация

ОпцияТипПо умолчаниюОписание
dbNamestring"blok-assets"IndexedDB database name.

Настройка хранилища

  • None — this preset needs no external service, which is the point.
  • Wire the exported resolveBlokObjectUrl(url) helper into wherever you render an uploaded asset: uploadByFile returns a blok:asset/… reference, not a directly usable URL, and nothing resolves it back into a blob: URL for you automatically.
  • If you pass a custom dbName to indexedDBStorage(), pass that same dbName as resolveBlokObjectUrl(url, { dbName }) — a mismatch looks up the wrong database and resolves to null with no error, not a thrown exception.

Пример использования

TypeScript
import { indexedDBStorage, resolveBlokObjectUrl } from '@bloklabs/presets';

new Blok({ holder: 'editor', uploader: indexedDBStorage() });

// Wherever you render a stored asset's url, e.g. an <img src>:
const displayUrl = await resolveBlokObjectUrl(asset.url);