Storage API Reference
直接上传、签名上传会话、对象查询和签名下载的接口。
Storage 将文件和产物保存到 S3 兼容对象存储中,并由 Cortex 管理元数据与权限。下面的 API 示例以 AI 生成的 Python、JavaScript 和 Java 客户端代码展示。
| 方法 | 路径 | Scope | 作用 |
|---|---|---|---|
POST | /v1/storage/files | storage:write | 小文件直接 multipart form 上传。 |
POST | /v1/storage/uploads | storage:write | 创建签名上传会话。 |
POST | /v1/storage/uploads/{uploadId}/complete | storage:write | 完成单分片或多分片上传会话。 |
GET | /v1/storage/objects/{objectId} | storage:read | 查看对象元数据和策略。 |
GET | /v1/storage/objects/{objectId}/download-url | storage:read | 创建签名下载 URL。 |
直接上传文件
POST /v1/storage/files 是 multipart/form-data 接口。
import osimport requestsBASE_URL = os.getenv("CORTEX_URL", "http://127.0.0.1:8080")TOKEN = os.getenv("CORTEX_TOKEN", "replace_with_token")def auth_headers(): return {"Authorization": f"Bearer {TOKEN}"}metadata = {"source": "storage-quickstart", "document_type": "guide"}access_policy = {"access_level": "tenant_shared"}with open("cortex-storage-quickstart.md", "w", encoding="utf-8") as f: f.write("# Cortex Storage\n\nThis file was uploaded through Cortex.")with open("cortex-storage-quickstart.md", "rb") as f: response = requests.post( f"{BASE_URL}/v1/storage/files", headers=auth_headers(), files={"file": ("cortex-storage-quickstart.md", f, "text/markdown")}, data={ "metadata_json": __import__("json").dumps(metadata), "access_policy_json": __import__("json").dumps(access_policy), "tags": "quickstart,docs", }, )response.raise_for_status()print(response.json())const BASE_URL = process.env.CORTEX_URL ?? "http://127.0.0.1:8080";const TOKEN = process.env.CORTEX_TOKEN ?? "replace_with_token";const authHeaders = { Authorization: `Bearer ${TOKEN}`,};const form = new FormData();form.append( "file", new Blob(["# Cortex Storage\n\nThis file was uploaded through Cortex."], { type: "text/markdown", }), "cortex-storage-quickstart.md",);form.append("metadata_json", JSON.stringify({ source: "storage-quickstart", document_type: "guide" }));form.append("access_policy_json", JSON.stringify({ access_level: "tenant_shared" }));form.append("tags", "quickstart,docs");const response = await fetch(`${BASE_URL}/v1/storage/files`, { method: "POST", headers: authHeaders, body: form,});if (!response.ok) throw new Error(await response.text());console.log(await response.json());import java.net.URI;import java.net.http.HttpClient;import java.net.http.HttpRequest;import java.net.http.HttpResponse;public class CortexExample { static final String BASE_URL = System.getenv().getOrDefault("CORTEX_URL", "http://127.0.0.1:8080"); static final String TOKEN = System.getenv().getOrDefault("CORTEX_TOKEN", "replace_with_token"); static final HttpClient HTTP = HttpClient.newHttpClient(); static void print(HttpResponse<String> response) { System.out.println(response.statusCode()); System.out.println(response.body()); } public static void main(String[] args) throws Exception { String boundary = "----CortexBoundary" + System.currentTimeMillis(); String body = "" + "--" + boundary + "\r\n" + "Content-Disposition: form-data; name=\"file\"; filename=\"cortex-storage-quickstart.md\"\r\n" + "Content-Type: text/markdown\r\n\r\n" + "# Cortex Storage\n\nThis file was uploaded through Cortex.\r\n" + "--" + boundary + "\r\n" + "Content-Disposition: form-data; name=\"metadata_json\"\r\n\r\n" + "{\"source\":\"storage-quickstart\",\"document_type\":\"guide\"}\r\n" + "--" + boundary + "\r\n" + "Content-Disposition: form-data; name=\"access_policy_json\"\r\n\r\n" + "{\"access_level\":\"tenant_shared\"}\r\n" + "--" + boundary + "\r\n" + "Content-Disposition: form-data; name=\"tags\"\r\n\r\n" + "quickstart,docs\r\n" + "--" + boundary + "--\r\n"; HttpRequest request = HttpRequest.newBuilder() .uri(URI.create(BASE_URL + "/v1/storage/files")) .header("Authorization", "Bearer " + TOKEN) .header("Content-Type", "multipart/form-data; boundary=" + boundary) .POST(HttpRequest.BodyPublishers.ofString(body)) .build(); print(HTTP.send(request, HttpResponse.BodyHandlers.ofString())); }}签名上传会话
import osimport requestsBASE_URL = os.getenv("CORTEX_URL", "http://127.0.0.1:8080")TOKEN = os.getenv("CORTEX_TOKEN", "replace_with_token")def auth_headers(): return {"Authorization": f"Bearer {TOKEN}"}payload = { "filename": "quarterly-report.pdf", "size_bytes": 67108864, "metadata": { "source": "finance-portal", "department": "fpna" }, "tags": [ "finance", "quarterly" ]}response = requests.post( f"{BASE_URL}/v1/storage/uploads", headers={**auth_headers(), "Content-Type": "application/json"}, json=payload,)response.raise_for_status()data = response.json()print(data)const BASE_URL = process.env.CORTEX_URL ?? "http://127.0.0.1:8080";const TOKEN = process.env.CORTEX_TOKEN ?? "replace_with_token";const authHeaders = { Authorization: `Bearer ${TOKEN}`,};const payload = { "filename": "quarterly-report.pdf", "size_bytes": 67108864, "metadata": { "source": "finance-portal", "department": "fpna" }, "tags": [ "finance", "quarterly" ]};const response = await fetch(`${BASE_URL}/v1/storage/uploads`, { method: "POST", headers: { ...authHeaders, "Content-Type": "application/json" }, body: JSON.stringify(payload),});if (!response.ok) throw new Error(await response.text());const data = await response.json();console.log(data);import java.net.URI;import java.net.http.HttpClient;import java.net.http.HttpRequest;import java.net.http.HttpResponse;public class CortexExample { static final String BASE_URL = System.getenv().getOrDefault("CORTEX_URL", "http://127.0.0.1:8080"); static final String TOKEN = System.getenv().getOrDefault("CORTEX_TOKEN", "replace_with_token"); static final HttpClient HTTP = HttpClient.newHttpClient(); static void print(HttpResponse<String> response) { System.out.println(response.statusCode()); System.out.println(response.body()); } public static void main(String[] args) throws Exception { String json = """ { \"filename\": \"quarterly-report.pdf\", \"size_bytes\": 67108864, \"metadata\": { \"source\": \"finance-portal\", \"department\": \"fpna\" }, \"tags\": [ \"finance\", \"quarterly\" ] } """; HttpRequest request = HttpRequest.newBuilder() .uri(URI.create(BASE_URL + "/v1/storage/uploads")) .header("Authorization", "Bearer " + TOKEN) .header("Content-Type", "application/json") .POST(HttpRequest.BodyPublishers.ofString(json)) .build(); print(HTTP.send(request, HttpResponse.BodyHandlers.ofString())); }}使用 provider ETag 完成 multipart upload:
import osimport requestsBASE_URL = os.getenv("CORTEX_URL", "http://127.0.0.1:8080")TOKEN = os.getenv("CORTEX_TOKEN", "replace_with_token")def auth_headers(): return {"Authorization": f"Bearer {TOKEN}"}payload = { "parts": [ { "part_number": 1, "etag": "\"part-1-etag\"" }, { "part_number": 2, "etag": "\"part-2-etag\"" } ], "checksum_sha256": "bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb"}response = requests.post( f"{BASE_URL}/v1/storage/uploads/upload_xxx/complete", headers={**auth_headers(), "Content-Type": "application/json"}, json=payload,)response.raise_for_status()data = response.json()print(data)const BASE_URL = process.env.CORTEX_URL ?? "http://127.0.0.1:8080";const TOKEN = process.env.CORTEX_TOKEN ?? "replace_with_token";const authHeaders = { Authorization: `Bearer ${TOKEN}`,};const payload = { "parts": [ { "part_number": 1, "etag": "\"part-1-etag\"" }, { "part_number": 2, "etag": "\"part-2-etag\"" } ], "checksum_sha256": "bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb"};const response = await fetch(`${BASE_URL}/v1/storage/uploads/upload_xxx/complete`, { method: "POST", headers: { ...authHeaders, "Content-Type": "application/json" }, body: JSON.stringify(payload),});if (!response.ok) throw new Error(await response.text());const data = await response.json();console.log(data);import java.net.URI;import java.net.http.HttpClient;import java.net.http.HttpRequest;import java.net.http.HttpResponse;public class CortexExample { static final String BASE_URL = System.getenv().getOrDefault("CORTEX_URL", "http://127.0.0.1:8080"); static final String TOKEN = System.getenv().getOrDefault("CORTEX_TOKEN", "replace_with_token"); static final HttpClient HTTP = HttpClient.newHttpClient(); static void print(HttpResponse<String> response) { System.out.println(response.statusCode()); System.out.println(response.body()); } public static void main(String[] args) throws Exception { String json = """ { \"parts\": [ { \"part_number\": 1, \"etag\": \"\\\"part-1-etag\\\"\" }, { \"part_number\": 2, \"etag\": \"\\\"part-2-etag\\\"\" } ], \"checksum_sha256\": \"bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb\" } """; HttpRequest request = HttpRequest.newBuilder() .uri(URI.create(BASE_URL + "/v1/storage/uploads/upload_xxx/complete")) .header("Authorization", "Bearer " + TOKEN) .header("Content-Type", "application/json") .POST(HttpRequest.BodyPublishers.ofString(json)) .build(); print(HTTP.send(request, HttpResponse.BodyHandlers.ofString())); }}查看和下载
import osimport requestsBASE_URL = os.getenv("CORTEX_URL", "http://127.0.0.1:8080")TOKEN = os.getenv("CORTEX_TOKEN", "replace_with_token")def auth_headers(): return {"Authorization": f"Bearer {TOKEN}"}response = requests.get(f"{BASE_URL}/v1/storage/objects/obj_xxx", headers=auth_headers())response.raise_for_status()data = response.json()print(data)const BASE_URL = process.env.CORTEX_URL ?? "http://127.0.0.1:8080";const TOKEN = process.env.CORTEX_TOKEN ?? "replace_with_token";const authHeaders = { Authorization: `Bearer ${TOKEN}`,};const response = await fetch(`${BASE_URL}/v1/storage/objects/obj_xxx`, { headers: authHeaders,});if (!response.ok) throw new Error(await response.text());const data = await response.json();console.log(data);import java.net.URI;import java.net.http.HttpClient;import java.net.http.HttpRequest;import java.net.http.HttpResponse;public class CortexExample { static final String BASE_URL = System.getenv().getOrDefault("CORTEX_URL", "http://127.0.0.1:8080"); static final String TOKEN = System.getenv().getOrDefault("CORTEX_TOKEN", "replace_with_token"); static final HttpClient HTTP = HttpClient.newHttpClient(); static void print(HttpResponse<String> response) { System.out.println(response.statusCode()); System.out.println(response.body()); } public static void main(String[] args) throws Exception { HttpRequest request = HttpRequest.newBuilder() .uri(URI.create(BASE_URL + "/v1/storage/objects/obj_xxx")) .header("Authorization", "Bearer " + TOKEN) .GET() .build(); print(HTTP.send(request, HttpResponse.BodyHandlers.ofString())); }}import osimport requestsBASE_URL = os.getenv("CORTEX_URL", "http://127.0.0.1:8080")TOKEN = os.getenv("CORTEX_TOKEN", "replace_with_token")def auth_headers(): return {"Authorization": f"Bearer {TOKEN}"}response = requests.get(f"{BASE_URL}/v1/storage/objects/obj_xxx/download-url?ttl_seconds=900", headers=auth_headers())response.raise_for_status()data = response.json()print(data)const BASE_URL = process.env.CORTEX_URL ?? "http://127.0.0.1:8080";const TOKEN = process.env.CORTEX_TOKEN ?? "replace_with_token";const authHeaders = { Authorization: `Bearer ${TOKEN}`,};const response = await fetch(`${BASE_URL}/v1/storage/objects/obj_xxx/download-url?ttl_seconds=900`, { headers: authHeaders,});if (!response.ok) throw new Error(await response.text());const data = await response.json();console.log(data);import java.net.URI;import java.net.http.HttpClient;import java.net.http.HttpRequest;import java.net.http.HttpResponse;public class CortexExample { static final String BASE_URL = System.getenv().getOrDefault("CORTEX_URL", "http://127.0.0.1:8080"); static final String TOKEN = System.getenv().getOrDefault("CORTEX_TOKEN", "replace_with_token"); static final HttpClient HTTP = HttpClient.newHttpClient(); static void print(HttpResponse<String> response) { System.out.println(response.statusCode()); System.out.println(response.body()); } public static void main(String[] args) throws Exception { HttpRequest request = HttpRequest.newBuilder() .uri(URI.create(BASE_URL + "/v1/storage/objects/obj_xxx/download-url?ttl_seconds=900")) .header("Authorization", "Bearer " + TOKEN) .GET() .build(); print(HTTP.send(request, HttpResponse.BodyHandlers.ofString())); }}