File Upload & Storage
파일 업로드, 검색, 메타데이터 관리를 위한 Storage API입니다.
파일 업로드가 필요한 경우
- Image-to-Image: 참조 이미지 기반 이미지 편집/변환
- Image-to-Video: 첫 프레임 이미지로 비디오 생성
- Voice Cloning: 음성 복제를 위한 샘플 오디오
- ControlNet: 포즈/깊이 맵 등 제어 이미지
1
Presigned URL 발급
먼저 파일 업로드를 위한 presigned URL을 발급받습니다. 이 시점에 파일 메타데이터가 스토리지에 등록됩니다.
curl -X POST https://api.core.today/v1/files/upload-url \
-H "X-API-Key: cdt_your_api_key" \
-H "Content-Type: application/json" \
-d '{"filename": "reference_image.png", "folder": "inputs"}'메타데이터 저장
커스텀 메타데이터를 함께 저장할 수 있습니다 (최대 10KB):
curl -X POST https://api.core.today/v1/files/upload-url \
-H "X-API-Key: cdt_your_api_key" \
-H "Content-Type: application/json" \
-d '{
"filename": "data",
"folder": "projects/my-project",
"metadata": {
"description": "프로젝트 데이터",
"tags": ["dataset", "training"],
"version": "1.0"
}
}'응답 예시
{
"upload_url": "https://s3.ap-northeast-2.amazonaws.com/files.core.today",
"upload_fields": {
"Content-Type": "image/png",
"key": "aiapi/f04423dfab11/inputs/1765720407215_reference_image.png",
"x-amz-algorithm": "AWS4-HMAC-SHA256",
"x-amz-credential": "ASIA.../20251214/ap-northeast-2/s3/aws4_request",
"x-amz-date": "20251214T120000Z",
"x-amz-security-token": "IQoJb3JpZ2luX2VjEG0a...",
"policy": "eyJleHBpcmF0aW9uIjoiMjAyNS0xMi0xNFQxMzowMDowMFoiLCJjb25kaXRpb25zIjpbey...",
"x-amz-signature": "e6444e667072a27fd5589241803c8abf..."
},
"file_url": "https://files.core.today/aiapi/.../reference_image.png?Expires=...&Signature=...&Key-Pair-Id=...",
"object_key": "aiapi/f04423dfab11/inputs/1765720407215_reference_image.png",
"folder": "inputs"
}2
S3에 파일 업로드
발급받은 upload_url과 upload_fields를 사용하여 S3에 직접 파일을 업로드합니다.
cURL
# upload_fields의 모든 필드를 순서대로 추가하고, file은 마지막에 추가
curl -X POST "https://s3.ap-northeast-2.amazonaws.com/files.core.today" \
-F "Content-Type=image/png" \
-F "key=aiapi/f04423dfab11/inputs/1765720407215_reference_image.png" \
-F "x-amz-algorithm=AWS4-HMAC-SHA256" \
-F "x-amz-credential=ASIA.../20251214/ap-northeast-2/s3/aws4_request" \
-F "x-amz-date=20251214T120000Z" \
-F "x-amz-security-token=IQoJb3JpZ2luX2VjEG0a..." \
-F "policy=eyJleHBpcmF0aW9uIjoiMjAyNS0xMi0xNFQxMzowMDowMFoiLCJjb25kaXRpb25zIjpbey..." \
-F "x-amz-signature=e6444e667072a27fd5589241803c8abf..." \
-F "file=@/path/to/reference_image.png"JavaScript
async function uploadFile(file, apiKey) {
// 1. Get presigned URL
const urlResponse = await fetch(
"https://api.core.today/v1/files/upload-url",
{
method: "POST",
headers: {
"X-API-Key": apiKey,
"Content-Type": "application/json"
},
body: JSON.stringify({ filename: file.name, folder: "inputs" })
}
);
const { upload_url, upload_fields, file_url } = await urlResponse.json();
// 2. Upload to S3
const formData = new FormData();
Object.entries(upload_fields).forEach(([key, value]) => {
formData.append(key, value);
});
formData.append("file", file); // file must be last!
await fetch(upload_url, {
method: "POST",
body: formData
});
// 3. Return the CDN URL (use this in prediction requests)
return file_url;
}Python
import requests
def upload_file(filepath, api_key, folder="inputs"):
filename = filepath.split("/")[-1]
# 1. Get presigned URL
url_response = requests.post(
"https://api.core.today/v1/files/upload-url",
headers={
"X-API-Key": api_key,
"Content-Type": "application/json"
},
json={"filename": filename, "folder": folder}
)
data = url_response.json()
# 2. Upload to S3
with open(filepath, "rb") as f:
files = {"file": (filename, f)}
requests.post(
data["upload_url"],
data=data["upload_fields"],
files=files
)
# 3. Return the CDN URL (use this in prediction requests)
return data["file_url"]중요
file필드는 반드시 마지막에 추가해야 합니다.- 모든
upload_fields값을 그대로 전달해야 합니다.
3
모델 입력에 사용
업로드 완료 후 file_url을 모델의 입력 파라미터에 사용합니다.
Image-to-Image (FLUX Kontext)
curl -X POST https://api.core.today/v1/predictions \
-H "X-API-Key: cdt_your_api_key" \
-H "Content-Type: application/json" \
-d '{
"model": "flux-kontext-pro",
"input": {
"prompt": "change the background to a beach sunset",
"image_url": "https://cdn.core.today/inputs/abc123/reference_image.png"
}
}'Image-to-Video (Kling)
curl -X POST https://api.core.today/v1/predictions \
-H "X-API-Key: cdt_your_api_key" \
-H "Content-Type: application/json" \
-d '{
"model": "kling-1.6-pro",
"input": {
"prompt": "camera slowly zooms in, gentle wind blowing",
"image": "https://cdn.core.today/inputs/abc123/first_frame.jpg",
"duration": 5
}
}'Voice Cloning (Fish Speech)
curl -X POST https://api.core.today/v1/predictions \
-H "X-API-Key: cdt_your_api_key" \
-H "Content-Type: application/json" \
-d '{
"model": "fish-speech-1.5",
"input": {
"text": "이 문장은 업로드한 음성과 똑같은 목소리로 읽힙니다.",
"reference_audio": "https://cdn.core.today/inputs/abc123/voice_sample.wav"
}
}'파일 검색 API
업로드된 파일을 다양한 조건으로 검색할 수 있습니다.
POST /files/storage/search
요청 파라미터
| 파라미터 | 타입 | 설명 |
|---|---|---|
| filters | object | 검색 조건 (메타데이터 필드 기반) |
| sort_by | string | 정렬 기준 필드 (기본값: created_at) |
| sort_order | string | "asc" 또는 "desc" (기본값: desc) |
| page | integer | 페이지 번호 (기본값: 1) |
| page_size | integer | 페이지당 결과 수 (기본값: 20, 최대: 100) |
검색 예제
# 최근 업로드된 PNG 파일 검색
curl -X POST https://api.core.today/v1/files/storage/search \
-H "X-API-Key: cdt_your_api_key" \
-H "Content-Type: application/json" \
-d '{
"filters": {
"content_type": "image/png",
"folder": "inputs"
},
"sort_by": "created_at",
"sort_order": "desc",
"page_size": 10
}'응답 예시
{
"files": [
{
"object_key": "aiapi/team123/inputs/1734175200000_photo.png",
"filename": "photo.png",
"content_type": "image/png",
"size": 1048576,
"folder": "inputs",
"created_at": "2025-12-14T10:00:00Z",
"url": "https://files.core.today/...",
"metadata": {
"project": "avatar-gen",
"tags": ["portrait", "input"]
}
}
],
"total": 42,
"page": 1,
"page_size": 10,
"total_pages": 5
}검색 가능한 메타데이터 필드
40개의 인덱싱된 필드를 사용하여 파일을 검색할 수 있습니다.
Identity 필드
file_id - 파일 고유 IDobject_key - S3 객체 키filename - 원본 파일명team_id - 팀 IDuser_id - 사용자 IDTime 필드
created_at - 생성 시간updated_at - 수정 시간expires_at - 만료 시간Source 필드
source - 출처 (upload/prediction)source_job_id - 원본 작업 IDsource_model - 생성 모델명upload_ip - 업로드 IPObject 필드
content_type - MIME 타입size - 파일 크기 (bytes)extension - 확장자folder - 폴더 경로checksum - MD5 해시Classification 필드
category - 카테고리subcategory - 서브카테고리tags - 태그 배열labels - 라벨 배열project - 프로젝트명environment - 환경 (dev/prod)Status 필드
status - 상태 (pending/active/archived)visibility - 공개 범위is_processed - 처리 완료 여부is_archived - 아카이브 여부Metrics 필드
width - 이미지/비디오 너비height - 이미지/비디오 높이duration - 오디오/비디오 길이(초)frame_count - 비디오 프레임 수sample_rate - 오디오 샘플레이트bit_depth - 오디오 비트심도AI/ML Context 필드
prompt - 생성 프롬프트negative_prompt - 네거티브 프롬프트seed - 랜덤 시드steps - 생성 스텝 수cfg_scale - CFG 스케일model_version - 모델 버전Compliance 필드
retention_days - 보관 기간legal_hold - 법적 보존 여부classification_level - 보안 등급고유값 조회 API
특정 필드의 고유값 목록을 조회합니다. 필터링 UI 구성에 유용합니다.
GET /files/storage/distinct/{field}
# 사용된 모든 폴더 목록 조회
curl -X GET "https://api.core.today/v1/files/storage/distinct/folder" \
-H "X-API-Key: cdt_your_api_key"
# 응답 예시
{
"field": "folder",
"values": ["inputs", "outputs", "projects/avatars", "projects/backgrounds"],
"count": 4
}지원 필드
folder, content_type, extension, category, project, tags, source_model, status 등
스키마 필드 조회 API
검색 가능한 모든 필드와 타입 정보를 조회합니다.
GET /files/storage/schema/fields
curl -X GET "https://api.core.today/v1/files/storage/schema/fields" \
-H "X-API-Key: cdt_your_api_key"
# 응답 예시
{
"fields": [
{"name": "file_id", "type": "keyword", "searchable": true},
{"name": "filename", "type": "text", "searchable": true},
{"name": "created_at", "type": "date", "searchable": true},
{"name": "size", "type": "long", "searchable": true},
{"name": "tags", "type": "keyword", "searchable": true, "array": true}
],
"total_fields": 40
}메타데이터 수정 API
파일의 메타데이터를 업데이트합니다. 기존 메타데이터와 병합됩니다.
PATCH /files/storage/file/metadata
curl -X PATCH https://api.core.today/v1/files/storage/file/metadata \
-H "X-API-Key: cdt_your_api_key" \
-H "Content-Type: application/json" \
-d '{
"object_key": "aiapi/team123/inputs/1734175200000_photo.png",
"metadata": {
"tags": ["portrait", "approved"],
"status": "reviewed",
"reviewer": "user@example.com"
}
}'
# 응답 예시
{
"success": true,
"object_key": "aiapi/team123/inputs/1734175200000_photo.png",
"updated_fields": ["tags", "status", "reviewer"],
"updated_at": "2025-12-14T12:00:00Z"
}주의사항
- 시스템 필드(file_id, team_id, created_at 등)는 수정 불가
- 메타데이터 전체 크기는 10KB를 초과할 수 없음
파일 삭제 API
파일과 메타데이터를 영구 삭제합니다.
DELETE /files/storage/file
curl -X DELETE "https://api.core.today/v1/files/storage/file?object_key=aiapi/team123/inputs/1734175200000_photo.png" \
-H "X-API-Key: cdt_your_api_key"
# 응답 예시
{
"success": true,
"deleted_object_key": "aiapi/team123/inputs/1734175200000_photo.png",
"deleted_at": "2025-12-14T12:00:00Z"
}경고
- 삭제된 파일은 복구할 수 없습니다
- 자신의 팀에 속한 파일만 삭제할 수 있습니다
SDK 예제
Python - 파일 검색
import requests
API_KEY = "cdt_your_api_key"
BASE_URL = "https://api.core.today/v1"
def search_files(filters, page=1, page_size=20):
"""파일을 검색합니다."""
response = requests.post(
f"{BASE_URL}/files/storage/search",
headers={
"X-API-Key": API_KEY,
"Content-Type": "application/json"
},
json={
"filters": filters,
"page": page,
"page_size": page_size,
"sort_by": "created_at",
"sort_order": "desc"
}
)
return response.json()
# 예: PNG 이미지 검색
results = search_files({
"content_type": "image/png",
"folder": "inputs"
})
for file in results["files"]:
print(f"{file['filename']} - {file['size']} bytes")
# 예: 특정 프로젝트의 파일 검색
project_files = search_files({
"project": "avatar-gen",
"tags": ["approved"]
})JavaScript - 파일 검색
const API_KEY = "cdt_your_api_key";
const BASE_URL = "https://api.core.today/v1";
async function searchFiles(filters, options = {}) {
const { page = 1, pageSize = 20, sortBy = "created_at", sortOrder = "desc" } = options;
const response = await fetch(`${BASE_URL}/files/storage/search`, {
method: "POST",
headers: {
"X-API-Key": API_KEY,
"Content-Type": "application/json"
},
body: JSON.stringify({
filters,
page,
page_size: pageSize,
sort_by: sortBy,
sort_order: sortOrder
})
});
return response.json();
}
// 예: 최근 업로드된 이미지 검색
const images = await searchFiles({
content_type: "image/png",
folder: "inputs"
});
console.log(`Found ${images.total} files`);
images.files.forEach(file => {
console.log(`${file.filename} - ${file.size} bytes`);
});
// 예: 특정 모델로 생성된 파일 검색
const generatedFiles = await searchFiles({
source_model: "flux-schnell",
created_at: { gte: "2025-12-01T00:00:00Z" }
});메타데이터 설계 가이드
프로젝트별 관리
{
"project": "avatar-generator",
"category": "portraits",
"environment": "production"
}워크플로우 추적
{
"status": "pending_review",
"tags": ["needs-approval", "high-priority"],
"assigned_to": "reviewer@company.com"
}AI 생성 컨텍스트
{
"source_model": "flux-schnell",
"prompt": "A professional headshot",
"seed": 12345,
"steps": 28,
"cfg_scale": 7.5
}베스트 프랙티스
- 일관된 네이밍: 모든 프로젝트에서 동일한 필드명 사용
- 태그 활용: 여러 분류가 필요한 경우 tags 배열 활용
- 환경 구분: dev/staging/production 환경별 파일 구분
- 버전 관리: version 필드로 파일 버전 추적
파일 업로드 제한
파일 형식
모든 파일 형식 지원
이미지, 비디오, 오디오, 문서, 바이너리 등
최대 크기
50MB
파일 유효 기간
| 항목 | 유효 기간 | 설명 |
|---|---|---|
| Presigned Upload URL | 7일 | URL 발급 후 7일 내 업로드 필요 |
| 업로드된 파일 | 7일 | 업로드 후 7일 뒤 자동 삭제 |
| 결과 파일 URL | 7일 | 모델 실행 결과 URL 유효 기간 |
중요
- 파일은 업로드 후 7일 뒤 자동으로 삭제됩니다
- 장기 보관이 필요한 파일은 결과를 별도로 저장하세요
- 스토리지 사용량은 대시보드 설정에서 확인할 수 있습니다
전체 예제 (Python)
import requests
import time
API_KEY = "cdt_your_api_key"
BASE_URL = "https://api.core.today/v1"
def upload_and_transform(image_path, prompt, project="default"):
"""이미지를 업로드하고 변환합니다."""
# 1. Get presigned URL with metadata
filename = image_path.split("/")[-1]
url_response = requests.post(
f"{BASE_URL}/files/upload-url",
headers={"X-API-Key": API_KEY, "Content-Type": "application/json"},
json={
"filename": filename,
"folder": f"projects/{project}/inputs",
"metadata": {
"project": project,
"source": "upload",
"original_filename": filename
}
}
)
url_data = url_response.json()
# 2. Upload to S3
with open(image_path, "rb") as f:
requests.post(
url_data["upload_url"],
data=url_data["upload_fields"],
files={"file": (filename, f)}
)
# 3. Create prediction with uploaded image
pred_response = requests.post(
f"{BASE_URL}/predictions",
headers={"X-API-Key": API_KEY, "Content-Type": "application/json"},
json={
"model": "flux-kontext-pro",
"input": {
"prompt": prompt,
"image_url": url_data["file_url"]
}
}
)
job_id = pred_response.json()["job_id"]
# 4. Poll for result
while True:
result = requests.get(
f"{BASE_URL}/predictions/{job_id}",
headers={"X-API-Key": API_KEY}
).json()
if result["status"] == "completed":
return result["result"]
elif result["status"] == "failed":
raise Exception(result.get("error"))
time.sleep(2)
# Usage
result = upload_and_transform(
"/path/to/photo.jpg",
"make it look like a watercolor painting",
project="art-styles"
)
print(f"Result: {result}")