文件传输
在本地存储、对象存储和沙箱之间传输文件
你可以在 Lybic 沙箱实例和外部存储之间复制文件。这对于上传数据集、脚本或下载结果文件非常有用。
API 端点
POST /api/orgs/{orgId}/sandboxes/{sandboxId}/file/copy请求格式
基本请求结构
curl -X POST "https://api.lybic.cn/api/orgs/{orgId}/sandboxes/{sandboxId}/file/copy" \
-H "Content-Type: application/json" \
-H "Authorization: Bearer YOUR_API_KEY" \
-d '{
"files": [
{
"id": "unique-identifier",
"src": { ... },
"dest": { ... }
}
]
}'import asyncio
from lybic import LybicClient, LybicAuth
from lybic.dto import (
SandboxFileCopyRequestDto,
FileCopyItem,
SandboxFileLocation,
HttpGetLocation
)
async def main():
async with LybicClient(
LybicAuth(
org_id="ORG-xxxx",
api_key="lysk-xxxxxxxxxxx",
endpoint="https://api.lybic.cn/"
)
) as client:
response = await client.sandbox.copy_files(
"SBX-xxxx",
SandboxFileCopyRequestDto(files=[
FileCopyItem(
id="unique-identifier",
src=HttpGetLocation(url="https://example.com/file.txt"),
dest=SandboxFileLocation(path="/home/agent/file.txt")
)
])
)
print("复制结果:", response)
if __name__ == "__main__":
asyncio.run(main())import { LybicClient } from '@lybic/core'
const lybic = new LybicClient({
baseUrl: 'https://api.lybic.cn',
orgId: 'ORG-xxxx',
apiKey: 'lysk-your-api-key-here',
})
const result = await lybic.copyFilesWithSandbox('SBX-xxxx', {
files: [
{
id: 'unique-identifier',
src: {
type: 'httpGetLocation',
url: 'https://example.com/file.txt',
},
dest: {
type: 'sandboxFileLocation',
path: '/home/agent/file.txt',
},
},
],
})
console.log('复制结果:', result.data)package main
import (
"context"
"fmt"
"github.com/lybic/lybic-sdk-go"
)
func main() {
ctx := context.Background()
client, _ := lybic.NewClient(nil)
copyDto := lybic.SandboxFileCopyRequestDto{
Files: []lybic.SandboxFileCopyRequestDtoFiles{
{
Src: map[string]string{
"type": "httpGetLocation",
"url": "https://example.com/file.txt",
},
Dest: map[string]any{
"type": "sandboxFileLocation",
"path": "/home/agent/file.txt",
},
},
},
}
result, err := client.CopyFilesWithSandbox(ctx, "SBX-xxxx", copyDto)
if err != nil {
fmt.Println("复制文件时出错:", err)
return
}
fmt.Printf("复制结果: %+v\n", result)
}请求体参数
files(array, 必需): 文件复制操作列表,至少包含一个项id(string, 可选): 调用方定义的唯一标识符,用于在响应中关联结果src(object, 必需): 文件源位置dest(object, 必需): 文件目标位置
支持的位置类型
1. SandboxFileLocation - 沙箱内文件路径
{
"type": "sandboxFileLocation",
"path": "/home/agent/file.txt"
}2. HttpGetLocation - HTTP GET 下载
{
"type": "httpGetLocation",
"url": "https://example.com/file.txt",
"headers": {
"Authorization": "Bearer token",
"X-Custom-Header": "value"
}
}3. HttpPutLocation - HTTP PUT 上传
{
"type": "httpPutLocation",
"url": "https://storage.example.com/file.txt",
"headers": {
"Content-Type": "application/octet-stream"
}
}4. HttpPostFormLocation - HTTP POST 多部分表单上传
{
"type": "httpPostFormLocation",
"url": "https://api.example.com/upload",
"form": {
"key": "value",
"policy": "eyJleHBpcmF0aW9uIjoi..."
},
"fileField": "file",
"headers": {
"X-Custom-Header": "value"
}
}响应格式
{
"results": [
{
"id": "unique-identifier",
"success": true,
"error": "error message if failed"
}
]
}使用场景示例
1. 本地文件与对象存储之间传输
1.1 上传本地文件到对象存储(使用 PUT)
# 第一步:获取预签名 PUT URL (示例使用 MinIO)
# 这通常通过对象存储 SDK 或 API 生成
# 第二步:上传文件到预签名 URL
curl -X PUT "https://storage.example.com/presigned-url" \
--upload-file local-file.txt1.2 从对象存储下载文件到本地(使用 GET)
# 第一步:获取预签名 GET URL (示例使用 MinIO)
# 这通常通过对象存储 SDK 或 API 生成
# 第二步:下载文件
curl -X GET "https://storage.example.com/presigned-url" \
-o downloaded-file.txt2. 本地文件与沙箱之间传输(通过对象存储)
2.1 从本地上传文件到沙箱
流程:本地文件 → 对象存储 → 沙箱
# 第一步:上传本地文件到对象存储
curl -X PUT "https://storage.example.com/presigned-put-url" \
--upload-file local-data.json
# 第二步:获取下载 URL (预签名 GET URL)
DOWNLOAD_URL="https://storage.example.com/presigned-get-url"
# 第三步:从 URL 下载文件到沙箱
curl -X POST "https://api.lybic.cn/api/orgs/ORG-xxx/sandboxes/BOX-xxx/file/copy" \
-H "Content-Type: application/json" \
-H "Authorization: Bearer lysk-xxxxxxxxx" \
-d '{
"files": [
{
"id": "upload-1",
"src": {
"type": "httpGetLocation",
"url": "'"$DOWNLOAD_URL"'"
},
"dest": {
"type": "sandboxFileLocation",
"path": "/home/agent/data.json"
}
}
]
}'2.2 从沙箱下载文件到本地
流程:沙箱 → 对象存储 → 本地文件
# 第一步:获取预签名 PUT URL 用于上传
UPLOAD_URL="https://storage.example.com/presigned-put-url"
# 第二步:将沙箱文件上传到对象存储
curl -X POST "https://api.lybic.cn/api/orgs/ORG-xxx/sandboxes/BOX-xxx/file/copy" \
-H "Content-Type: application/json" \
-H "Authorization: Bearer lysk-xxxxxxxxx" \
-d '{
"files": [
{
"id": "download-1",
"src": {
"type": "sandboxFileLocation",
"path": "/home/agent/output.txt"
},
"dest": {
"type": "httpPutLocation",
"url": "'"$UPLOAD_URL"'"
}
}
]
}'
# 第三步:从对象存储下载到本地
curl -X GET "https://storage.example.com/presigned-get-url" \
-o local-output.txt3. 沙箱与对象存储之间传输
3.1 从对象存储下载文件到沙箱
# 单文件下载
curl -X POST "https://api.lybic.cn/api/orgs/ORG-xxx/sandboxes/BOX-xxx/file/copy" \
-H "Content-Type: application/json" \
-H "Authorization: Bearer lysk-xxxxxxxxx" \
-d '{
"files": [
{
"id": "download-dataset",
"src": {
"type": "httpGetLocation",
"url": "https://storage.example.com/datasets/training-data.csv"
},
"dest": {
"type": "sandboxFileLocation",
"path": "/home/agent/datasets/training-data.csv"
}
}
]
}'import asyncio
from lybic import LybicClient, LybicAuth
from lybic.dto import (
SandboxFileCopyRequestDto,
FileCopyItem,
SandboxFileLocation,
HttpGetLocation
)
async def main():
async with LybicClient(
LybicAuth(
org_id="ORG-xxx",
api_key="lysk-xxxxxxxxx",
endpoint="https://api.lybic.cn/"
)
) as client:
response = await client.sandbox.copy_files(
"BOX-xxx",
SandboxFileCopyRequestDto(files=[
FileCopyItem(
id="download-dataset",
src=HttpGetLocation(
url="https://storage.example.com/datasets/training-data.csv"
),
dest=SandboxFileLocation(
path="/home/agent/datasets/training-data.csv"
)
)
])
)
for result in response.results:
if result.success:
print(f"✓ 文件下载成功 (id: {result.id})")
else:
print(f"✗ 下载失败 (id: {result.id}): {result.error}")
if __name__ == "__main__":
asyncio.run(main())import { LybicClient } from '@lybic/core'
const lybic = new LybicClient({
baseUrl: 'https://api.lybic.cn',
orgId: 'ORG-xxx',
apiKey: 'lysk-xxxxxxxxx',
})
const result = await lybic.copyFilesWithSandbox('BOX-xxx', {
files: [
{
id: 'download-dataset',
src: {
type: 'httpGetLocation',
url: 'https://storage.example.com/datasets/training-data.csv',
},
dest: {
type: 'sandboxFileLocation',
path: '/home/agent/datasets/training-data.csv',
},
},
],
})
result.data?.results?.forEach((r) => {
if (r.success) {
console.log(`✓ 文件下载成功 (id: ${r.id})`)
} else {
console.log(`✗ 下载失败 (id: ${r.id}): ${r.error}`)
}
})package main
import (
"context"
"fmt"
"github.com/lybic/lybic-sdk-go"
)
func main() {
ctx := context.Background()
client, _ := lybic.NewClient(nil)
copyDto := lybic.SandboxFileCopyRequestDto{
Files: []lybic.SandboxFileCopyRequestDtoFiles{
{
Id: "download-dataset",
Src: map[string]string{
"type": "httpGetLocation",
"url": "https://storage.example.com/datasets/training-data.csv",
},
Dest: map[string]any{
"type": "sandboxFileLocation",
"path": "/home/agent/datasets/training-data.csv",
},
},
},
}
result, err := client.CopyFilesWithSandbox(ctx, "BOX-xxx", copyDto)
if err != nil {
fmt.Println("复制文件时出错:", err)
return
}
for _, r := range result.Results {
if r.Success {
fmt.Printf("✓ 文件下载成功 (id: %s)\n", r.Id)
} else {
fmt.Printf("✗ 下载失败 (id: %s): %s\n", r.Id, r.Error)
}
}
}3.2 从沙箱上传文件到对象存储
# 单文件上传
curl -X POST "https://api.lybic.cn/api/orgs/ORG-xxx/sandboxes/BOX-xxx/file/copy" \
-H "Content-Type: application/json" \
-H "Authorization: Bearer lysk-xxxxxxxxx" \
-d '{
"files": [
{
"id": "upload-result",
"src": {
"type": "sandboxFileLocation",
"path": "/home/agent/results/model-output.pkl"
},
"dest": {
"type": "httpPutLocation",
"url": "https://storage.example.com/presigned-put-url"
}
}
]
}'import asyncio
from lybic import LybicClient, LybicAuth
from lybic.dto import (
SandboxFileCopyRequestDto,
FileCopyItem,
SandboxFileLocation,
HttpPutLocation
)
async def main():
async with LybicClient(
LybicAuth(
org_id="ORG-xxx",
api_key="lysk-xxxxxxxxx",
endpoint="https://api.lybic.cn/"
)
) as client:
response = await client.sandbox.copy_files(
"BOX-xxx",
SandboxFileCopyRequestDto(files=[
FileCopyItem(
id="upload-result",
src=SandboxFileLocation(
path="/home/agent/results/model-output.pkl"
),
dest=HttpPutLocation(
url="https://storage.example.com/presigned-put-url"
)
)
])
)
for result in response.results:
if result.success:
print(f"✓ 文件上传成功 (id: {result.id})")
else:
print(f"✗ 上传失败 (id: {result.id}): {result.error}")
if __name__ == "__main__":
asyncio.run(main())import { LybicClient } from '@lybic/core'
const lybic = new LybicClient({
baseUrl: 'https://api.lybic.cn',
orgId: 'ORG-xxx',
apiKey: 'lysk-xxxxxxxxx',
})
const result = await lybic.copyFilesWithSandbox('BOX-xxx', {
files: [
{
id: 'upload-result',
src: {
type: 'sandboxFileLocation',
path: '/home/agent/results/model-output.pkl',
},
dest: {
type: 'httpPutLocation',
url: 'https://storage.example.com/presigned-put-url',
},
},
],
})
result.data?.results?.forEach((r) => {
if (r.success) {
console.log(`✓ 文件上传成功 (id: ${r.id})`)
} else {
console.log(`✗ 上传失败 (id: ${r.id}): ${r.error}`)
}
})package main
import (
"context"
"fmt"
"github.com/lybic/lybic-sdk-go"
)
func main() {
ctx := context.Background()
client, _ := lybic.NewClient(nil)
copyDto := lybic.SandboxFileCopyRequestDto{
Files: []lybic.SandboxFileCopyRequestDtoFiles{
{
Id: "upload-result",
Src: map[string]string{
"type": "sandboxFileLocation",
"path": "/home/agent/results/model-output.pkl",
},
Dest: map[string]any{
"type": "httpPutLocation",
"url": "https://storage.example.com/presigned-put-url",
},
},
},
}
result, err := client.CopyFilesWithSandbox(ctx, "BOX-xxx", copyDto)
if err != nil {
fmt.Println("复制文件时出错:", err)
return
}
for _, r := range result.Results {
if r.Success {
fmt.Printf("✓ 文件上传成功 (id: %s)\n", r.Id)
} else {
fmt.Printf("✗ 上传失败 (id: %s): %s\n", r.Id, r.Error)
}
}
}完整端到端示例可以参考:Python SDK
4. 沙箱内文件复制
在沙箱内部复制文件(源和目标都是沙箱路径):
curl -X POST "https://api.lybic.cn/api/orgs/ORG-xxx/sandboxes/BOX-xxx/file/copy" \
-H "Content-Type: application/json" \
-H "Authorization: Bearer lysk-xxxxxxxxx" \
-d '{
"files": [
{
"id": "copy-config",
"src": {
"type": "sandboxFileLocation",
"path": "/home/agent/config/template.yaml"
},
"dest": {
"type": "sandboxFileLocation",
"path": "/home/agent/config/production.yaml"
}
}
]
}'import asyncio
from lybic import LybicClient, LybicAuth
from lybic.dto import (
SandboxFileCopyRequestDto,
FileCopyItem,
SandboxFileLocation
)
async def main():
async with LybicClient(
LybicAuth(
org_id="ORG-xxx",
api_key="lysk-xxxxxxxxx",
endpoint="https://api.lybic.cn/"
)
) as client:
response = await client.sandbox.copy_files(
"BOX-xxx",
SandboxFileCopyRequestDto(files=[
FileCopyItem(
id="copy-config",
src=SandboxFileLocation(path="/home/agent/config/template.yaml"),
dest=SandboxFileLocation(path="/home/agent/config/production.yaml")
)
])
)
print("复制结果:", response)
if __name__ == "__main__":
asyncio.run(main())import { LybicClient } from '@lybic/core'
const lybic = new LybicClient({
baseUrl: 'https://api.lybic.cn',
orgId: 'ORG-xxx',
apiKey: 'lysk-xxxxxxxxx',
})
const result = await lybic.copyFilesWithSandbox('BOX-xxx', {
files: [
{
id: 'copy-config',
src: {
type: 'sandboxFileLocation',
path: '/home/agent/config/template.yaml',
},
dest: {
type: 'sandboxFileLocation',
path: '/home/agent/config/production.yaml',
},
},
],
})
console.log('复制结果:', result.data)package main
import (
"context"
"fmt"
"github.com/lybic/lybic-sdk-go"
)
func main() {
ctx := context.Background()
client, _ := lybic.NewClient(nil)
copyDto := lybic.SandboxFileCopyRequestDto{
Files: []lybic.SandboxFileCopyRequestDtoFiles{
{
Id: "copy-config",
Src: map[string]string{
"type": "sandboxFileLocation",
"path": "/home/agent/config/template.yaml",
},
Dest: map[string]any{
"type": "sandboxFileLocation",
"path": "/home/agent/config/production.yaml",
},
},
},
}
result, err := client.CopyFilesWithSandbox(ctx, "BOX-xxx", copyDto)
if err != nil {
fmt.Println("复制文件时出错:", err)
return
}
fmt.Printf("复制结果: %+v\n", result)
}5. 下载文件到沙箱特定位置(下载功能)
5.1 从公共 URL 下载
# 下载公共文件
curl -X POST "https://api.lybic.cn/api/orgs/ORG-xxx/sandboxes/BOX-xxx/file/copy" \
-H "Content-Type: application/json" \
-H "Authorization: Bearer lysk-xxxxxxxxx" \
-d '{
"files": [
{
"id": "download-public-file",
"src": {
"type": "httpGetLocation",
"url": "https://raw.githubusercontent.com/example/repo/main/data.json"
},
"dest": {
"type": "sandboxFileLocation",
"path": "/home/agent/downloads/data.json"
}
}
]
}'5.2 从需要认证的 URL 下载
# 使用自定义请求头下载(例如:私有存储或 API)
curl -X POST "https://api.lybic.cn/api/orgs/ORG-xxx/sandboxes/BOX-xxx/file/copy" \
-H "Content-Type: application/json" \
-H "Authorization: Bearer lysk-xxxxxxxxx" \
-d '{
"files": [
{
"id": "download-private-file",
"src": {
"type": "httpGetLocation",
"url": "https://api.example.com/files/dataset.csv",
"headers": {
"Authorization": "Bearer your-token-here",
"X-API-Key": "your-api-key"
}
},
"dest": {
"type": "sandboxFileLocation",
"path": "/home/agent/data/dataset.csv"
}
}
]
}'批量文件传输
多文件下载到沙箱
curl -X POST "https://api.lybic.cn/api/orgs/ORG-xxx/sandboxes/BOX-xxx/file/copy" \
-H "Content-Type: application/json" \
-H "Authorization: Bearer lysk-xxxxxxxxx" \
-d '{
"files": [
{
"id": "file-1",
"src": {
"type": "httpGetLocation",
"url": "https://storage.example.com/file1.txt"
},
"dest": {
"type": "sandboxFileLocation",
"path": "/home/agent/file1.txt"
}
},
{
"id": "file-2",
"src": {
"type": "httpGetLocation",
"url": "https://storage.example.com/file2.json"
},
"dest": {
"type": "sandboxFileLocation",
"path": "/home/agent/file2.json"
}
},
{
"id": "file-3",
"src": {
"type": "httpGetLocation",
"url": "https://storage.example.com/file3.csv"
},
"dest": {
"type": "sandboxFileLocation",
"path": "/home/agent/data/file3.csv"
}
}
]
}'混合方向的批量传输
在一个请求中同时上传和下载多个文件:
curl -X POST "https://api.lybic.cn/api/orgs/ORG-xxx/sandboxes/BOX-xxx/file/copy" \
-H "Content-Type: application/json" \
-H "Authorization: Bearer lysk-xxxxxxxxx" \
-d '{
"files": [
{
"id": "download-input",
"src": {
"type": "httpGetLocation",
"url": "https://storage.example.com/input-data.csv"
},
"dest": {
"type": "sandboxFileLocation",
"path": "/home/agent/input.csv"
}
},
{
"id": "upload-result",
"src": {
"type": "sandboxFileLocation",
"path": "/home/agent/output.json"
},
"dest": {
"type": "httpPutLocation",
"url": "https://storage.example.com/presigned-put-url-1"
}
},
{
"id": "upload-logs",
"src": {
"type": "sandboxFileLocation",
"path": "/home/agent/logs/process.log"
},
"dest": {
"type": "httpPutLocation",
"url": "https://storage.example.com/presigned-put-url-2"
}
}
]
}'响应示例
成功响应:
{
"results": [
{
"id": "download-input",
"success": true
},
{
"id": "upload-result",
"success": true
},
{
"id": "upload-logs",
"success": true
}
]
}部分失败响应:
{
"results": [
{
"id": "file-1",
"success": true
},
{
"id": "file-2",
"success": false,
"error": "Failed to download file: 404 Not Found"
},
{
"id": "file-3",
"success": true
}
]
}使用 POST 表单上传
某些对象存储服务(如 AWS S3)需要使用 POST 表单进行上传:
# 从沙箱使用 POST 表单上传到对象存储
curl -X POST "https://api.lybic.cn/api/orgs/ORG-xxx/sandboxes/BOX-xxx/file/copy" \
-H "Content-Type: application/json" \
-H "Authorization: Bearer lysk-xxxxxxxxx" \
-d '{
"files": [
{
"id": "form-upload",
"src": {
"type": "sandboxFileLocation",
"path": "/home/agent/report.pdf"
},
"dest": {
"type": "httpPostFormLocation",
"url": "https://s3.amazonaws.com/your-bucket",
"form": {
"key": "uploads/report.pdf",
"AWSAccessKeyId": "YOUR_ACCESS_KEY",
"policy": "BASE64_ENCODED_POLICY",
"signature": "CALCULATED_SIGNATURE"
},
"fileField": "file"
}
}
]
}'可选的请求头设置
常见请求头示例
1. 认证头
{
"type": "httpGetLocation",
"url": "https://api.example.com/file",
"headers": {
"Authorization": "Bearer YOUR_ACCESS_TOKEN"
}
}2. API 密钥认证
{
"type": "httpGetLocation",
"url": "https://api.example.com/file",
"headers": {
"X-API-Key": "your-api-key-here"
}
}3. 内容类型设置
{
"type": "httpPutLocation",
"url": "https://storage.example.com/file",
"headers": {
"Content-Type": "application/json"
}
}4. 自定义头组合
{
"type": "httpGetLocation",
"url": "https://api.example.com/file",
"headers": {
"Authorization": "Bearer token",
"X-Request-ID": "unique-request-id",
"User-Agent": "Lybic-Agent/1.0",
"Accept": "application/octet-stream"
}
}完整示例:使用自定义请求头
curl -X POST "https://api.lybic.cn/api/orgs/ORG-xxx/sandboxes/BOX-xxx/file/copy" \
-H "Content-Type: application/json" \
-H "Authorization: Bearer lysk-xxxxxxxxx" \
-d '{
"files": [
{
"id": "authenticated-download",
"src": {
"type": "httpGetLocation",
"url": "https://secure-api.example.com/protected/file.dat",
"headers": {
"Authorization": "Bearer secure-token-12345",
"X-Client-ID": "client-id",
"X-Request-ID": "req-abc-123"
}
},
"dest": {
"type": "sandboxFileLocation",
"path": "/home/agent/secure/file.dat"
}
},
{
"id": "authenticated-upload",
"src": {
"type": "sandboxFileLocation",
"path": "/home/agent/output/result.json"
},
"dest": {
"type": "httpPutLocation",
"url": "https://storage.example.com/upload-endpoint",
"headers": {
"Content-Type": "application/json",
"X-Upload-Token": "upload-token-xyz",
"X-Checksum": "md5-hash-value"
}
}
}
]
}'最佳实践
- 使用唯一的 ID:为每个文件操作设置唯一的
id,以便在批量操作中跟踪结果 - 处理错误:始终检查响应中的
success字段和error消息 - 预签名 URL 过期:确保预签名 URL 有足够的有效期(建议至少 1 小时)
- 批量操作:尽可能在单个请求中批量处理多个文件以提高效率
- 请求头安全:避免在请求头中硬编码敏感信息,使用环境变量或安全存储
- 路径验证:确保沙箱目标路径存在或创建必要的父目录
- 大文件传输:对于大文件,考虑使用流式传输或分块上传
常见错误
| 错误 | 原因 | 解决方案 |
|---|---|---|
| 404 Not Found | URL 不存在或已过期 | 检查 URL 是否正确,重新生成预签名 URL |
| 403 Forbidden | 权限不足或认证失败 | 验证认证头和权限设置 |
| 400 Bad Request | 请求格式错误 | 检查 JSON 格式和必需字段 |
| File not found in sandbox | 沙箱中源文件不存在 | 验证文件路径是否正确 |
| Network timeout | 网络超时或文件过大 | 增加超时时间或分块传输 |