Lielu failu augšupielāde
Failu vienā POST /v1/cards pieprasījumā var nosūtīt līdz instalācijas
augšupielādes limitam (pēc noklusējuma 200 MB). Lielākus failus, kā arī tīklā, kur garš pieprasījums, visticamāk,
pārtrūks, augšupielādējiet pa daļām:
- Izveidojiet augšupielādi: paziņojiet Sursai faila nosaukumu, tipu un izmēru.
- Nosūtiet daļas jebkurā secībā, ja vēlaties – paralēli. Neizdevusies daļa tiek nosūtīta vēlreiz atsevišķi.
- Pabeidziet augšupielādi ar visa faila SHA-256. Sursa saliek failu un pārbauda to.
- Izmantojiet augšupielādes ID faila vietā: kartītei, arhīvam vai pielāgota avota dokumentam.
| Limits | Vērtība |
|---|---|
| Faila izmērs | Līdz 5 GB |
| Daļas izmērs | To izvēlas Sursa laukā part_size, no 5 MiB līdz 100 MiB; pēdējā daļa var būt mazāka |
| Daļu skaits | Līdz 1000 |
| Laiks pabeigšanai | 24 stundas pēc augšupielādes izveides |
| Laiks pabeigtas augšupielādes izmantošanai | 24 stundas |
1. Izveidojiet augšupielādi
POST /v1/uploads pieņem faila nosaukumu, tipu un izmēru, kā arī to, kam
failu izmantosiet:
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 | Augšupielāde tiks izmantota |
|---|---|
card_file | Kartītei: POST /v1/cards. |
archive | Dokumentu arhīvam: POST /v1/cards/archive. |
import_attachment | Pielāgota avota dokumenta failam laukā upload_id vai attachments. |
Atslēgai vajadzīgs tās vietas tvērums, kur augšupielāde tiks izmantota: cards:write vērtībām card_file un
archive, import:write vērtībai import_attachment.
Sursa izvēlas part_size un paziņo, cik daļu jānosūta. Ja faila SHA-256 jums ir zināms jau iepriekš, varat to nodot
arī šeit kā sha256.
2. Nosūtiet daļas
Sadaliet failu part_count gabalos pa part_size baitiem (pēdējais ir atlikums) un katru nosūtiet ar
PUT /v1/uploads/{upload_id}/parts/{n}. Daļas numurē no 1. Saturs ir daļas
neapstrādātie baiti.
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))
doneAtkārtoti nosūtīta daļa aizstāj iepriekšējo, tāpēc daļu, kuras nosūtīšana neizdevās vai beidzās ar noildzi, vienkārši nosūta vēlreiz. Ātru savienojumu parasti pilnībā noslogo līdz 4 paralēli sūtītām daļām.
Pārbaudiet daļas
Katra daļa iet caur API: tiešu saišu uz krātuvi nav. Atbildē uz daļu ir tās galvene ETag – daļas SHA-256 pēdiņās –
un tā pati vērtība JSON satura laukā etag. GET /v1/uploads/{upload_id} laukā
parts uzskaita līdz šim saņemtās daļas, tāpēc pēc pārtraukuma jūs nosūtāt tikai trūkstošās.
3. Pabeidziet augšupielādi
POST /v1/uploads/{upload_id}/complete pieņem visa faila SHA-256 kā
64 heksadecimālas rakstzīmes un, ja vēlaties, arī katras daļas etag laukā parts, lai pārbaudītu arī daļas:
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\"}"Sursa saliek daļas, salīdzina jaucējvērtību un pārbauda failu tāpat kā jebkuru augšupielādi: tā formātu un, ja tas ir
ieslēgts, ar pretvīrusu programmu. Atbilde ir 200, kad pārbaude ir pabeigta, un 202, kamēr tā vēl notiek.
Augšupielādes statuss mainās no processing uz ready vai uz rejected ar iemeslu laukā reject_reason. Nolasiet
to ar GET /v1/uploads/{upload_id}.
4. Izmantojiet augšupielādi
Nododiet upload_id tur, kur sūtītu failu. Katra augšupielāde tiek izmantota vienreiz: pēc tam tās statuss ir
consumed.
Kartītei un arhīvam nosūtiet upload_id JSON saturā, nevis multipart/form-data formā. Šajā saturā kolekciju
norāda ar tās nosaukumu laukā collection.
# 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\"}"Pilns augšupielādes rīks
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));
}
}Kļūdas
| Kods | Statuss | Cēlonis |
|---|---|---|
uploads.too_large | 413 | Fails ir lielāks par 5 GB vai par visu organizācijas plāna krātuves apjomu. |
limits.storage | 402 | Organizācijai nav pietiekami daudz brīvas krātuves šim failam. |
uploads.part_invalid | 400 | Daļas numurs ir ārpus diapazona, vai daļas izmērs nav atļautajās robežās. |
uploads.checksum_mismatch | 422 | Saliktā faila SHA-256 nesakrīt ar nosūtīto. Kāda daļa ir bojāta vai trūkst: nosūtiet daļas vēlreiz un vēlreiz pabeidziet augšupielādi. |
uploads.not_ready | 409 | Augšupielāde vēl nav pabeigta un pārbaudīta. Gaidiet ready. |
uploads.already_used | 409 | Augšupielāde jau ir izmantota. Katru augšupielādi izmanto vienreiz. |
uploads.expired | 410 | Pagājušas vairāk nekā 24 stundas. Izveidojiet jaunu augšupielādi. |
uploads.rejected | 422 | Fails neizturēja pārbaudes, piemēram, tā formāts neatbilst nosaukumam. Ziņojumā ir norādīts iemesls. |
uploads.not_found | 404 | Organizācijā šādas augšupielādes nav. |