Nagy fájlok feltöltése
Egy fájl a telepítés feltöltési korlátjáig (alapértelmezés szerint 200 MB) egyetlen
POST /v1/cards kérésben is elküldhető. Ennél nagyobb fájlt, vagy olyan hálózaton,
ahol egy hosszú kérés valószínűleg megszakad, részenként tölts fel:
- Hozz létre egy feltöltést: add meg a Sursának a fájl nevét, típusát és méretét.
- Küldd el a részeket, tetszőleges sorrendben, akár párhuzamosan is. A sikertelen részt önmagában küldöd el újra.
- Fejezd be a feltöltést a teljes fájl SHA-256 értékével. A Sursa összeállítja és ellenőrzi a fájlt.
- A fájl helyett a feltöltés azonosítóját használd: kártyához, archívumhoz vagy egy egyéni forrás dokumentumához.
| Korlát | Érték |
|---|---|
| Fájlméret | Legfeljebb 5 GB |
| Részméret | A Sursa választja meg a part_size mezőben, 5 MiB–100 MiB; az utolsó rész kisebb is lehet |
| Részek száma | Legfeljebb 1000 |
| A befejezés határideje | A feltöltés létrehozása után 24 óra |
| A befejezett feltöltés felhasználásának határideje | 24 óra |
1. A feltöltés létrehozása
A POST /v1/uploads a fájl nevét, típusát és méretét várja, valamint azt, hogy
mire fogod használni:
FILE=training-video-transcripts.zip
SIZE=$(wc -c < "$FILE" | tr -d ' ')
curl -X POST https://api.sursa.com/v1/uploads \
-H "Authorization: Bearer $SURSA_API_KEY" \
-H "Idempotency-Key: $(uuidgen)" \
-H "Content-Type: application/json" \
-d "{\"filename\": \"$FILE\", \"content_type\": \"application/zip\", \"size\": $SIZE, \"purpose\": \"archive\"}"{
"id": "upl_5d3c2b1a0f9e8d7c6b5a493827161504",
"status": "pending",
"part_size": 8388608,
"part_count": 23,
"expires_at": "2026-10-25T10:00:00Z"
}
purpose | A feltöltés rendeltetése |
|---|---|
card_file | Kártya: POST /v1/cards. |
archive | Dokumentumarchívum: POST /v1/cards/archive. |
import_attachment | Egy egyéni forrás dokumentumának fájlja, az upload_id vagy az attachments mezőben. |
A kulcsnak annak a helynek a hatókörével kell rendelkeznie, ahová a feltöltés kerül: a card_file és az archive
rendeltetéshez cards:write, az import_attachment rendeltetéshez import:write hatókörrel.
A part_size értékét a Sursa választja meg, és megadja, hány részt kell elküldened. Ha előre ismered a fájl SHA-256
értékét, itt is megadhatod sha256 mezőként.
2. A részek elküldése
Vágd a fájlt part_count darab, egyenként part_size bájtos részre (az utolsó a maradék), és mindegyiket küldd el
a PUT /v1/uploads/{upload_id}/parts/{n} végponttal. A részek számozása 1-től
kezdődik. A törzs a rész nyers bájtjai.
UPLOAD=upl_5d3c2b1a0f9e8d7c6b5a493827161504
split -b 8388608 -a 4 "$FILE" part-
n=1
for part in part-*; do
curl -X PUT "https://api.sursa.com/v1/uploads/$UPLOAD/parts/$n" \
-H "Authorization: Bearer $SURSA_API_KEY" \
-H "Content-Type: application/octet-stream" \
--data-binary "@$part" --retry 5 --fail
n=$((n + 1))
doneEgy rész újbóli elküldése lecseréli azt, így a sikertelen vagy időtúllépéssel megszakadt részt egyszerűen újra elküldöd. Legfeljebb 4 párhuzamos rész általában kitölt egy gyors kapcsolatot.
A részek ellenőrzése
Minden rész az API-n keresztül érkezik: a tárhelyre mutató közvetlen hivatkozások nincsenek. A részre adott válasz
ETag fejléce a rész idézőjelek közé tett SHA-256 értéke, és ugyanez az érték szerepel a JSON-törzs etag mezőjében
is. A GET /v1/uploads/{upload_id} a parts mezőben felsorolja az eddig beérkezett
részeket, így megszakadás után csak a hiányzókat kell elküldened.
3. A feltöltés befejezése
A POST /v1/uploads/{upload_id}/complete a teljes fájl SHA-256 értékét várja
64 hexadecimális karakterként, és opcionálisan a parts mezőben az egyes részek etag értékét is, hogy a részeket
is ellenőrizze:
SHA=$(shasum -a 256 "$FILE" | cut -d' ' -f1)
curl -X POST "https://api.sursa.com/v1/uploads/$UPLOAD/complete" \
-H "Authorization: Bearer $SURSA_API_KEY" \
-H "Idempotency-Key: complete-$UPLOAD" \
-H "Content-Type: application/json" \
-d "{\"sha256\": \"$SHA\"}"A Sursa összeállítja a részeket, összeveti a hash-t, és úgy ellenőrzi a fájlt, mint bármely feltöltést: a formátumát
és, ahol be van kapcsolva, víruskereséssel. A válasz 200, ha az ellenőrzés kész, és 202, amíg még fut. A feltöltés
állapota processing értékről ready értékre vált, vagy rejected értékre, az indoklással a reject_reason
mezőben. A GET /v1/uploads/{upload_id} végponttal olvashatod ki.
4. A feltöltés használata
Add meg az upload_id azonosítót ott, ahol a fájlt küldenéd. Minden feltöltés egyszer használható fel: ezután az
állapota consumed.
Kártyánál és archívumnál az upload_id azonosítót a multipart/form-data űrlap helyett JSON-törzsben küldd el. Ez a
törzs a gyűjteményt a nevével várja, a collection mezőben.
# A card from the uploaded file
curl -X POST https://api.sursa.com/v1/cards \
-H "Authorization: Bearer $SURSA_API_KEY" \
-H "Idempotency-Key: card-from-$UPLOAD" \
-H "Content-Type: application/json" \
-d "{\"upload_id\": \"$UPLOAD\", \"collection\": \"support\", \"async\": true}"
# An archive of documents from the uploaded ZIP
curl -X POST https://api.sursa.com/v1/cards/archive \
-H "Authorization: Bearer $SURSA_API_KEY" \
-H "Idempotency-Key: archive-from-$UPLOAD" \
-H "Content-Type: application/json" \
-d "{\"upload_id\": \"$UPLOAD\", \"collection\": \"support\"}"Egy teljes feltöltő
import { createHash } from "node:crypto";
import { createReadStream } from "node:fs";
import { open, stat } from "node:fs/promises";
import { basename } from "node:path";
const BASE = "https://api.sursa.com";
const auth = { Authorization: `Bearer ${process.env.SURSA_API_KEY}` };
async function call(method, path, body, headers = {}) {
const isJson = body !== undefined && !(body instanceof Uint8Array);
const response = await fetch(BASE + path, {
method,
headers: { ...auth, ...(isJson ? { "Content-Type": "application/json" } : {}), ...headers },
body: isJson ? JSON.stringify(body) : body,
});
if (!response.ok) throw new Error(`${method} ${path}: HTTP ${response.status} ${await response.text()}`);
return response.json();
}
export async function uploadLargeFile(file, contentType, purpose, parallel = 4) {
const { size } = await stat(file);
const upload = await call("POST", "/v1/uploads",
{ filename: basename(file), content_type: contentType, size, purpose },
{ "Idempotency-Key": crypto.randomUUID() });
const hash = createHash("sha256");
for await (const block of createReadStream(file)) hash.update(block);
const handle = await open(file);
let next = 1;
async function worker() {
for (let n = next++; n <= upload.part_count; n = next++) {
const buffer = new Uint8Array(Math.min(upload.part_size, size - (n - 1) * upload.part_size));
await handle.read(buffer, 0, buffer.length, (n - 1) * upload.part_size);
for (let attempt = 1; ; attempt++) {
try {
await call("PUT", `/v1/uploads/${upload.id}/parts/${n}`, buffer, { "Content-Type": "application/octet-stream" });
break;
} catch (error) {
if (attempt === 5) throw error;
await new Promise((resolve) => setTimeout(resolve, 1000 * 2 ** attempt));
}
}
}
}
await Promise.all(Array.from({ length: parallel }, worker));
await handle.close();
await call("POST", `/v1/uploads/${upload.id}/complete`, { sha256: hash.digest("hex") },
{ "Idempotency-Key": `complete-${upload.id}` });
for (;;) {
const state = await call("GET", `/v1/uploads/${upload.id}`);
if (state.status === "ready") return upload.id;
if (state.status === "rejected") throw new Error(`upload rejected: ${state.reject_reason}`);
await new Promise((resolve) => setTimeout(resolve, 2000));
}
}Hibák
| Kód | Állapotkód | Ok |
|---|---|---|
uploads.too_large | 413 | A fájl nagyobb 5 GB-nál, vagy nagyobb a szervezet csomagjának teljes tárhelyénél. |
limits.storage | 402 | A szervezetnek nem maradt elég szabad tárhelye a fájlhoz. |
uploads.part_invalid | 400 | A rész sorszáma a tartományon kívül esik, vagy a rész mérete nem megengedett. |
uploads.checksum_mismatch | 422 | Az összeállított fájl SHA-256 értéke nem az, amit elküldtél. Egy rész megsérült vagy hiányzik: küldd el újra a részeket, és fejezd be újra a feltöltést. |
uploads.not_ready | 409 | A feltöltés még nincs befejezve és ellenőrizve. Várd meg a ready állapotot. |
uploads.already_used | 409 | A feltöltést már felhasználták. Minden feltöltés egyszer használható fel. |
uploads.expired | 410 | Több mint 24 óra telt el. Hozz létre új feltöltést. |
uploads.rejected | 422 | A fájl nem ment át az ellenőrzéseken, például a formátuma nem egyezik a nevével. Az üzenet megmondja, miért. |
uploads.not_found | 404 | Nincs ilyen feltöltés a szervezetben. |