बड़ी फ़ाइलों का अपलोड
फ़ाइल POST /v1/cards की एक ही रिक्वेस्ट में इंस्टॉलेशन की अपलोड सीमा (डिफ़ॉल्ट
रूप से 200 MB) तक जा सकती है। इससे बड़ी फ़ाइल हो, या ऐसा नेटवर्क हो, जहाँ लंबी रिक्वेस्ट के टूटने की संभावना है, तो
फ़ाइल हिस्सों में अपलोड करें:
- अपलोड बनाएँ: Sursa को फ़ाइल का नाम, टाइप और आकार बताएँ।
- हिस्से भेजें, किसी भी क्रम में, और चाहें तो समानांतर। असफल हिस्सा अकेले फिर से भेजा जाता है।
- पूरी फ़ाइल के SHA-256 के साथ अपलोड पूरा करें। Sursa फ़ाइल को जोड़ता है और उसकी जाँच करता है।
- फ़ाइल की जगह अपलोड की ID इस्तेमाल करें: कार्ड, आर्काइव, या कस्टम स्रोत के दस्तावेज़ के लिए।
| सीमा | मान |
|---|---|
| फ़ाइल का आकार | 5 GB तक |
| हिस्से का आकार | Sursa इसे part_size में चुनता है, 5 MiB से 100 MiB तक; आख़िरी हिस्सा छोटा हो सकता है |
| हिस्सों की संख्या | 1000 तक |
| पूरा करने का समय | अपलोड बनने के बाद 24 घंटे |
| पूरे हुए अपलोड को इस्तेमाल करने का समय | 24 घंटे |
1. अपलोड बनाएँ
POST /v1/uploads फ़ाइल का नाम, टाइप और आकार लेता है, और यह कि आप उसे किसलिए
इस्तेमाल करने वाले हैं:
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 | अपलोड किसलिए इस्तेमाल होगा |
|---|---|
card_file | कार्ड: POST /v1/cards। |
archive | दस्तावेज़ों का आर्काइव: POST /v1/cards/archive। |
import_attachment | कस्टम स्रोत के दस्तावेज़ की फ़ाइल, upload_id या attachments में। |
कुंजी के पास उस जगह का स्कोप होना चाहिए, जहाँ अपलोड जाता है: card_file और archive के लिए cards:write,
import_attachment के लिए import:write।
Sursa part_size चुनता है और बताता है कि कितने हिस्से भेजने हैं। अगर आपको फ़ाइल का SHA-256 पहले से पता है, तो आप उसे
यहाँ भी sha256 के रूप में भेज सकते हैं।
2. हिस्से भेजें
फ़ाइल को part_size बाइट के part_count हिस्सों में काटें (आख़िरी हिस्से में बाक़ी बचा भाग) और हर एक को
PUT /v1/uploads/{upload_id}/parts/{n} से भेजें। हिस्सों की गिनती 1 से होती है।
बॉडी हिस्से के रॉ बाइट होती है।
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))
doneहिस्सा फिर से भेजने पर वह बदल जाता है, इसलिए जो हिस्सा असफल रहा या जिसका टाइमआउट हो गया, उसे बस फिर से भेज दिया जाता है। समानांतर 4 हिस्से तक आम तौर पर तेज़ कनेक्शन को पूरा भर देते हैं।
हिस्से जाँचें
हर हिस्सा API से होकर जाता है: स्टोरेज के सीधे लिंक नहीं हैं। हिस्से के रिस्पॉन्स में उसका ETag हेडर होता है, यानी
उद्धरण चिह्नों में हिस्से का SHA-256, और JSON बॉडी के etag में वही मान।
GET /v1/uploads/{upload_id} अब तक मिले हिस्सों की सूची parts में देता है, इसलिए
रुकावट के बाद आप सिर्फ़ ग़ायब हिस्से भेजते हैं।
3. अपलोड पूरा करें
POST /v1/uploads/{upload_id}/complete पूरी फ़ाइल का SHA-256 64 हेक्साडेसिमल
अक्षरों के रूप में लेता है, और, चाहें तो, parts में हर हिस्से का etag भी, ताकि हिस्सों की भी जाँच हो:
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 हिस्सों को जोड़ता है, हैश की तुलना करता है, और फ़ाइल की वैसी ही जाँच करता है जैसी किसी भी अपलोड की: उसका
फ़ॉर्मेट और, जहाँ चालू है, एंटीवायरस। जाँच पूरी होने पर जवाब 200 होता है, और जब तक वह चल रही है, तब तक 202। अपलोड
का स्टेटस processing से ready हो जाता है, या rejected, जिसका कारण reject_reason में होता है। इसे
GET /v1/uploads/{upload_id} से पढ़ें।
4. अपलोड इस्तेमाल करें
जहाँ आप फ़ाइल भेजते, वहाँ upload_id भेजें। हर अपलोड एक बार इस्तेमाल होता है: उसके बाद उसका स्टेटस consumed
होता है।
कार्ड और आर्काइव के लिए upload_id को multipart/form-data फ़ॉर्म के बजाय JSON बॉडी में भेजें। यह बॉडी कलेक्शन को
उसके नाम से 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\"}"पूरा अपलोडर
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));
}
}एरर
| कोड | स्टेटस | कारण |
|---|---|---|
uploads.too_large | 413 | फ़ाइल 5 GB से बड़ी है, या संगठन के प्लान के पूरे स्टोरेज से बड़ी है। |
limits.storage | 402 | संगठन के पास फ़ाइल के लिए पर्याप्त ख़ाली स्टोरेज नहीं बचा है। |
uploads.part_invalid | 400 | हिस्से का नंबर सीमा से बाहर है, या हिस्सा मान्य आकार से बाहर है। |
uploads.checksum_mismatch | 422 | जोड़ी गई फ़ाइल का SHA-256 वह नहीं है, जो आपने भेजा। कोई हिस्सा ख़राब हो गया है या ग़ायब है: हिस्से फिर से भेजें और अपलोड फिर से पूरा करें। |
uploads.not_ready | 409 | अपलोड अभी पूरा और जाँचा नहीं गया है। ready का इंतज़ार करें। |
uploads.already_used | 409 | अपलोड पहले ही इस्तेमाल हो चुका है। हर अपलोड एक बार इस्तेमाल होता है। |
uploads.expired | 410 | 24 घंटे से ज़्यादा बीत चुके हैं। नया अपलोड बनाएँ। |
uploads.rejected | 422 | फ़ाइल अपनी जाँच में पास नहीं हुई, उदाहरण के लिए उसका फ़ॉर्मेट उसके नाम से मेल नहीं खाता। संदेश बताता है कि क्यों। |
uploads.not_found | 404 | संगठन में ऐसा कोई अपलोड नहीं है। |