feat: add Service API file preview endpoint (#23534)

Co-authored-by: autofix-ci[bot] <114827586+autofix-ci[bot]@users.noreply.github.com>
This commit is contained in:
lyzno1
2025-08-07 21:44:29 +08:00
committed by GitHub
parent ac02c12e49
commit d98071a088
16 changed files with 1495 additions and 7 deletions

View File

@@ -6,6 +6,6 @@ bp = Blueprint("service_api", __name__, url_prefix="/v1")
api = ExternalApi(bp)
from . import index
from .app import annotation, app, audio, completion, conversation, file, message, site, workflow
from .app import annotation, app, audio, completion, conversation, file, file_preview, message, site, workflow
from .dataset import dataset, document, hit_testing, metadata, segment, upload_file
from .workspace import models

View File

@@ -107,3 +107,15 @@ class UnsupportedFileTypeError(BaseHTTPException):
error_code = "unsupported_file_type"
description = "File type not allowed."
code = 415
class FileNotFoundError(BaseHTTPException):
error_code = "file_not_found"
description = "The requested file was not found."
code = 404
class FileAccessDeniedError(BaseHTTPException):
error_code = "file_access_denied"
description = "Access to the requested file is denied."
code = 403

View File

@@ -0,0 +1,186 @@
import logging
from urllib.parse import quote
from flask import Response
from flask_restful import Resource, reqparse
from controllers.service_api import api
from controllers.service_api.app.error import (
FileAccessDeniedError,
FileNotFoundError,
)
from controllers.service_api.wraps import FetchUserArg, WhereisUserArg, validate_app_token
from extensions.ext_database import db
from extensions.ext_storage import storage
from models.model import App, EndUser, Message, MessageFile, UploadFile
logger = logging.getLogger(__name__)
class FilePreviewApi(Resource):
"""
Service API File Preview endpoint
Provides secure file preview/download functionality for external API users.
Files can only be accessed if they belong to messages within the requesting app's context.
"""
@validate_app_token(fetch_user_arg=FetchUserArg(fetch_from=WhereisUserArg.QUERY))
def get(self, app_model: App, end_user: EndUser, file_id: str):
"""
Preview/Download a file that was uploaded via Service API
Args:
app_model: The authenticated app model
end_user: The authenticated end user (optional)
file_id: UUID of the file to preview
Query Parameters:
user: Optional user identifier
as_attachment: Boolean, whether to download as attachment (default: false)
Returns:
Stream response with file content
Raises:
FileNotFoundError: File does not exist
FileAccessDeniedError: File access denied (not owned by app)
"""
file_id = str(file_id)
# Parse query parameters
parser = reqparse.RequestParser()
parser.add_argument("as_attachment", type=bool, required=False, default=False, location="args")
args = parser.parse_args()
# Validate file ownership and get file objects
message_file, upload_file = self._validate_file_ownership(file_id, app_model.id)
# Get file content generator
try:
generator = storage.load(upload_file.key, stream=True)
except Exception as e:
raise FileNotFoundError(f"Failed to load file content: {str(e)}")
# Build response with appropriate headers
response = self._build_file_response(generator, upload_file, args["as_attachment"])
return response
def _validate_file_ownership(self, file_id: str, app_id: str) -> tuple[MessageFile, UploadFile]:
"""
Validate that the file belongs to a message within the requesting app's context
Security validations performed:
1. File exists in MessageFile table (was used in a conversation)
2. Message belongs to the requesting app
3. UploadFile record exists and is accessible
4. File tenant matches app tenant (additional security layer)
Args:
file_id: UUID of the file to validate
app_id: UUID of the requesting app
Returns:
Tuple of (MessageFile, UploadFile) if validation passes
Raises:
FileNotFoundError: File or related records not found
FileAccessDeniedError: File does not belong to the app's context
"""
try:
# Input validation
if not file_id or not app_id:
raise FileAccessDeniedError("Invalid file or app identifier")
# First, find the MessageFile that references this upload file
message_file = db.session.query(MessageFile).where(MessageFile.upload_file_id == file_id).first()
if not message_file:
raise FileNotFoundError("File not found in message context")
# Get the message and verify it belongs to the requesting app
message = (
db.session.query(Message).where(Message.id == message_file.message_id, Message.app_id == app_id).first()
)
if not message:
raise FileAccessDeniedError("File access denied: not owned by requesting app")
# Get the actual upload file record
upload_file = db.session.query(UploadFile).where(UploadFile.id == file_id).first()
if not upload_file:
raise FileNotFoundError("Upload file record not found")
# Additional security: verify tenant isolation
app = db.session.query(App).where(App.id == app_id).first()
if app and upload_file.tenant_id != app.tenant_id:
raise FileAccessDeniedError("File access denied: tenant mismatch")
return message_file, upload_file
except (FileNotFoundError, FileAccessDeniedError):
# Re-raise our custom exceptions
raise
except Exception as e:
# Log unexpected errors for debugging
logger.exception(
"Unexpected error during file ownership validation",
extra={"file_id": file_id, "app_id": app_id, "error": str(e)},
)
raise FileAccessDeniedError("File access validation failed")
def _build_file_response(self, generator, upload_file: UploadFile, as_attachment: bool = False) -> Response:
"""
Build Flask Response object with appropriate headers for file streaming
Args:
generator: File content generator from storage
upload_file: UploadFile database record
as_attachment: Whether to set Content-Disposition as attachment
Returns:
Flask Response object with streaming file content
"""
response = Response(
generator,
mimetype=upload_file.mime_type,
direct_passthrough=True,
headers={},
)
# Add Content-Length if known
if upload_file.size and upload_file.size > 0:
response.headers["Content-Length"] = str(upload_file.size)
# Add Accept-Ranges header for audio/video files to support seeking
if upload_file.mime_type in [
"audio/mpeg",
"audio/wav",
"audio/mp4",
"audio/ogg",
"audio/flac",
"audio/aac",
"video/mp4",
"video/webm",
"video/quicktime",
"audio/x-m4a",
]:
response.headers["Accept-Ranges"] = "bytes"
# Set Content-Disposition for downloads
if as_attachment and upload_file.name:
encoded_filename = quote(upload_file.name)
response.headers["Content-Disposition"] = f"attachment; filename*=UTF-8''{encoded_filename}"
# Override content-type for downloads to force download
response.headers["Content-Type"] = "application/octet-stream"
# Add caching headers for performance
response.headers["Cache-Control"] = "public, max-age=3600" # Cache for 1 hour
return response
# Register the API endpoint
api.add_resource(FilePreviewApi, "/files/<uuid:file_id>/preview")

View File

@@ -0,0 +1,336 @@
"""
Unit tests for Service API File Preview endpoint
"""
import uuid
from unittest.mock import Mock, patch
import pytest
from controllers.service_api.app.error import FileAccessDeniedError, FileNotFoundError
from controllers.service_api.app.file_preview import FilePreviewApi
from models.model import App, EndUser, Message, MessageFile, UploadFile
class TestFilePreviewApi:
"""Test suite for FilePreviewApi"""
@pytest.fixture
def file_preview_api(self):
"""Create FilePreviewApi instance for testing"""
return FilePreviewApi()
@pytest.fixture
def mock_app(self):
"""Mock App model"""
app = Mock(spec=App)
app.id = str(uuid.uuid4())
app.tenant_id = str(uuid.uuid4())
return app
@pytest.fixture
def mock_end_user(self):
"""Mock EndUser model"""
end_user = Mock(spec=EndUser)
end_user.id = str(uuid.uuid4())
return end_user
@pytest.fixture
def mock_upload_file(self):
"""Mock UploadFile model"""
upload_file = Mock(spec=UploadFile)
upload_file.id = str(uuid.uuid4())
upload_file.name = "test_file.jpg"
upload_file.mime_type = "image/jpeg"
upload_file.size = 1024
upload_file.key = "storage/key/test_file.jpg"
upload_file.tenant_id = str(uuid.uuid4())
return upload_file
@pytest.fixture
def mock_message_file(self):
"""Mock MessageFile model"""
message_file = Mock(spec=MessageFile)
message_file.id = str(uuid.uuid4())
message_file.upload_file_id = str(uuid.uuid4())
message_file.message_id = str(uuid.uuid4())
return message_file
@pytest.fixture
def mock_message(self):
"""Mock Message model"""
message = Mock(spec=Message)
message.id = str(uuid.uuid4())
message.app_id = str(uuid.uuid4())
return message
def test_validate_file_ownership_success(
self, file_preview_api, mock_app, mock_upload_file, mock_message_file, mock_message
):
"""Test successful file ownership validation"""
file_id = str(uuid.uuid4())
app_id = mock_app.id
# Set up the mocks
mock_upload_file.tenant_id = mock_app.tenant_id
mock_message.app_id = app_id
mock_message_file.upload_file_id = file_id
mock_message_file.message_id = mock_message.id
with patch("controllers.service_api.app.file_preview.db") as mock_db:
# Mock database queries
mock_db.session.query.return_value.where.return_value.first.side_effect = [
mock_message_file, # MessageFile query
mock_message, # Message query
mock_upload_file, # UploadFile query
mock_app, # App query for tenant validation
]
# Execute the method
result_message_file, result_upload_file = file_preview_api._validate_file_ownership(file_id, app_id)
# Assertions
assert result_message_file == mock_message_file
assert result_upload_file == mock_upload_file
def test_validate_file_ownership_file_not_found(self, file_preview_api):
"""Test file ownership validation when MessageFile not found"""
file_id = str(uuid.uuid4())
app_id = str(uuid.uuid4())
with patch("controllers.service_api.app.file_preview.db") as mock_db:
# Mock MessageFile not found
mock_db.session.query.return_value.where.return_value.first.return_value = None
# Execute and assert exception
with pytest.raises(FileNotFoundError) as exc_info:
file_preview_api._validate_file_ownership(file_id, app_id)
assert "File not found in message context" in str(exc_info.value)
def test_validate_file_ownership_access_denied(self, file_preview_api, mock_message_file):
"""Test file ownership validation when Message not owned by app"""
file_id = str(uuid.uuid4())
app_id = str(uuid.uuid4())
with patch("controllers.service_api.app.file_preview.db") as mock_db:
# Mock MessageFile found but Message not owned by app
mock_db.session.query.return_value.where.return_value.first.side_effect = [
mock_message_file, # MessageFile query - found
None, # Message query - not found (access denied)
]
# Execute and assert exception
with pytest.raises(FileAccessDeniedError) as exc_info:
file_preview_api._validate_file_ownership(file_id, app_id)
assert "not owned by requesting app" in str(exc_info.value)
def test_validate_file_ownership_upload_file_not_found(self, file_preview_api, mock_message_file, mock_message):
"""Test file ownership validation when UploadFile not found"""
file_id = str(uuid.uuid4())
app_id = str(uuid.uuid4())
with patch("controllers.service_api.app.file_preview.db") as mock_db:
# Mock MessageFile and Message found but UploadFile not found
mock_db.session.query.return_value.where.return_value.first.side_effect = [
mock_message_file, # MessageFile query - found
mock_message, # Message query - found
None, # UploadFile query - not found
]
# Execute and assert exception
with pytest.raises(FileNotFoundError) as exc_info:
file_preview_api._validate_file_ownership(file_id, app_id)
assert "Upload file record not found" in str(exc_info.value)
def test_validate_file_ownership_tenant_mismatch(
self, file_preview_api, mock_app, mock_upload_file, mock_message_file, mock_message
):
"""Test file ownership validation with tenant mismatch"""
file_id = str(uuid.uuid4())
app_id = mock_app.id
# Set up tenant mismatch
mock_upload_file.tenant_id = "different_tenant_id"
mock_app.tenant_id = "app_tenant_id"
mock_message.app_id = app_id
mock_message_file.upload_file_id = file_id
mock_message_file.message_id = mock_message.id
with patch("controllers.service_api.app.file_preview.db") as mock_db:
# Mock database queries
mock_db.session.query.return_value.where.return_value.first.side_effect = [
mock_message_file, # MessageFile query
mock_message, # Message query
mock_upload_file, # UploadFile query
mock_app, # App query for tenant validation
]
# Execute and assert exception
with pytest.raises(FileAccessDeniedError) as exc_info:
file_preview_api._validate_file_ownership(file_id, app_id)
assert "tenant mismatch" in str(exc_info.value)
def test_validate_file_ownership_invalid_input(self, file_preview_api):
"""Test file ownership validation with invalid input"""
# Test with empty file_id
with pytest.raises(FileAccessDeniedError) as exc_info:
file_preview_api._validate_file_ownership("", "app_id")
assert "Invalid file or app identifier" in str(exc_info.value)
# Test with empty app_id
with pytest.raises(FileAccessDeniedError) as exc_info:
file_preview_api._validate_file_ownership("file_id", "")
assert "Invalid file or app identifier" in str(exc_info.value)
def test_build_file_response_basic(self, file_preview_api, mock_upload_file):
"""Test basic file response building"""
mock_generator = Mock()
response = file_preview_api._build_file_response(mock_generator, mock_upload_file, False)
# Check response properties
assert response.mimetype == mock_upload_file.mime_type
assert response.direct_passthrough is True
assert response.headers["Content-Length"] == str(mock_upload_file.size)
assert "Cache-Control" in response.headers
def test_build_file_response_as_attachment(self, file_preview_api, mock_upload_file):
"""Test file response building with attachment flag"""
mock_generator = Mock()
response = file_preview_api._build_file_response(mock_generator, mock_upload_file, True)
# Check attachment-specific headers
assert "attachment" in response.headers["Content-Disposition"]
assert mock_upload_file.name in response.headers["Content-Disposition"]
assert response.headers["Content-Type"] == "application/octet-stream"
def test_build_file_response_audio_video(self, file_preview_api, mock_upload_file):
"""Test file response building for audio/video files"""
mock_generator = Mock()
mock_upload_file.mime_type = "video/mp4"
response = file_preview_api._build_file_response(mock_generator, mock_upload_file, False)
# Check Range support for media files
assert response.headers["Accept-Ranges"] == "bytes"
def test_build_file_response_no_size(self, file_preview_api, mock_upload_file):
"""Test file response building when size is unknown"""
mock_generator = Mock()
mock_upload_file.size = 0 # Unknown size
response = file_preview_api._build_file_response(mock_generator, mock_upload_file, False)
# Content-Length should not be set when size is unknown
assert "Content-Length" not in response.headers
@patch("controllers.service_api.app.file_preview.storage")
def test_get_method_integration(
self, mock_storage, file_preview_api, mock_app, mock_end_user, mock_upload_file, mock_message_file, mock_message
):
"""Test the full GET method integration (without decorator)"""
file_id = str(uuid.uuid4())
app_id = mock_app.id
# Set up mocks
mock_upload_file.tenant_id = mock_app.tenant_id
mock_message.app_id = app_id
mock_message_file.upload_file_id = file_id
mock_message_file.message_id = mock_message.id
mock_generator = Mock()
mock_storage.load.return_value = mock_generator
with patch("controllers.service_api.app.file_preview.db") as mock_db:
# Mock database queries
mock_db.session.query.return_value.where.return_value.first.side_effect = [
mock_message_file, # MessageFile query
mock_message, # Message query
mock_upload_file, # UploadFile query
mock_app, # App query for tenant validation
]
with patch("controllers.service_api.app.file_preview.reqparse") as mock_reqparse:
# Mock request parsing
mock_parser = Mock()
mock_parser.parse_args.return_value = {"as_attachment": False}
mock_reqparse.RequestParser.return_value = mock_parser
# Test the core logic directly without Flask decorators
# Validate file ownership
result_message_file, result_upload_file = file_preview_api._validate_file_ownership(file_id, app_id)
assert result_message_file == mock_message_file
assert result_upload_file == mock_upload_file
# Test file response building
response = file_preview_api._build_file_response(mock_generator, mock_upload_file, False)
assert response is not None
# Verify storage was called correctly
mock_storage.load.assert_not_called() # Since we're testing components separately
@patch("controllers.service_api.app.file_preview.storage")
def test_storage_error_handling(
self, mock_storage, file_preview_api, mock_app, mock_upload_file, mock_message_file, mock_message
):
"""Test storage error handling in the core logic"""
file_id = str(uuid.uuid4())
app_id = mock_app.id
# Set up mocks
mock_upload_file.tenant_id = mock_app.tenant_id
mock_message.app_id = app_id
mock_message_file.upload_file_id = file_id
mock_message_file.message_id = mock_message.id
# Mock storage error
mock_storage.load.side_effect = Exception("Storage error")
with patch("controllers.service_api.app.file_preview.db") as mock_db:
# Mock database queries for validation
mock_db.session.query.return_value.where.return_value.first.side_effect = [
mock_message_file, # MessageFile query
mock_message, # Message query
mock_upload_file, # UploadFile query
mock_app, # App query for tenant validation
]
# First validate file ownership works
result_message_file, result_upload_file = file_preview_api._validate_file_ownership(file_id, app_id)
assert result_message_file == mock_message_file
assert result_upload_file == mock_upload_file
# Test storage error handling
with pytest.raises(Exception) as exc_info:
mock_storage.load(mock_upload_file.key, stream=True)
assert "Storage error" in str(exc_info.value)
@patch("controllers.service_api.app.file_preview.logger")
def test_validate_file_ownership_unexpected_error_logging(self, mock_logger, file_preview_api):
"""Test that unexpected errors are logged properly"""
file_id = str(uuid.uuid4())
app_id = str(uuid.uuid4())
with patch("controllers.service_api.app.file_preview.db") as mock_db:
# Mock database query to raise unexpected exception
mock_db.session.query.side_effect = Exception("Unexpected database error")
# Execute and assert exception
with pytest.raises(FileAccessDeniedError) as exc_info:
file_preview_api._validate_file_ownership(file_id, app_id)
# Verify error message
assert "File access validation failed" in str(exc_info.value)
# Verify logging was called
mock_logger.exception.assert_called_once_with(
"Unexpected error during file ownership validation",
extra={"file_id": file_id, "app_id": app_id, "error": "Unexpected database error"},
)

View File

@@ -277,6 +277,85 @@ The text generation application offers non-session support and is ideal for tran
</Row>
---
<Heading
url='/files/:file_id/preview'
method='GET'
title='File Preview'
name='#file-preview'
/>
<Row>
<Col>
Preview or download uploaded files. This endpoint allows you to access files that have been previously uploaded via the File Upload API.
<i>Files can only be accessed if they belong to messages within the requesting application.</i>
### Path Parameters
- `file_id` (string) Required
The unique identifier of the file to preview, obtained from the File Upload API response.
### Query Parameters
- `as_attachment` (boolean) Optional
Whether to force download the file as an attachment. Default is `false` (preview in browser).
### Response
Returns the file content with appropriate headers for browser display or download.
- `Content-Type` Set based on file mime type
- `Content-Length` File size in bytes (if available)
- `Content-Disposition` Set to "attachment" if `as_attachment=true`
- `Cache-Control` Caching headers for performance
- `Accept-Ranges` Set to "bytes" for audio/video files
### Errors
- 400, `invalid_param`, abnormal parameter input
- 403, `file_access_denied`, file access denied or file does not belong to current application
- 404, `file_not_found`, file not found or has been deleted
- 500, internal server error
</Col>
<Col sticky>
### Request Example
<CodeGroup title="Request" tag="GET" label="/files/:file_id/preview" targetCode={`curl -X GET '${props.appDetail.api_base_url}/files/72fa9618-8f89-4a37-9b33-7e1178a24a67/preview' \\\n--header 'Authorization: Bearer {api_key}'`}>
```bash {{ title: 'cURL' }}
curl -X GET '${props.appDetail.api_base_url}/files/72fa9618-8f89-4a37-9b33-7e1178a24a67/preview' \
--header 'Authorization: Bearer {api_key}'
```
</CodeGroup>
### Download as Attachment
<CodeGroup title="Download Request" tag="GET" label="/files/:file_id/preview?as_attachment=true" targetCode={`curl -X GET '${props.appDetail.api_base_url}/files/72fa9618-8f89-4a37-9b33-7e1178a24a67/preview?as_attachment=true' \\\n--header 'Authorization: Bearer {api_key}' \\\n--output downloaded_file.png`}>
```bash {{ title: 'cURL' }}
curl -X GET '${props.appDetail.api_base_url}/files/72fa9618-8f89-4a37-9b33-7e1178a24a67/preview?as_attachment=true' \
--header 'Authorization: Bearer {api_key}' \
--output downloaded_file.png
```
</CodeGroup>
### Response Headers Example
<CodeGroup title="Response Headers">
```http {{ title: 'Headers - Image Preview' }}
Content-Type: image/png
Content-Length: 1024
Cache-Control: public, max-age=3600
```
</CodeGroup>
### Download Response Headers
<CodeGroup title="Download Response Headers">
```http {{ title: 'Headers - File Download' }}
Content-Type: image/png
Content-Length: 1024
Content-Disposition: attachment; filename*=UTF-8''example.png
Cache-Control: public, max-age=3600
```
</CodeGroup>
</Col>
</Row>
---
<Heading
url='/completion-messages/:task_id/stop'
method='POST'

View File

@@ -276,6 +276,85 @@ import { Row, Col, Properties, Property, Heading, SubProperty, Paragraph } from
</Row>
---
<Heading
url='/files/:file_id/preview'
method='GET'
title='ファイルプレビュー'
name='#file-preview'
/>
<Row>
<Col>
アップロードされたファイルをプレビューまたはダウンロードします。このエンドポイントを使用すると、以前にファイルアップロード API でアップロードされたファイルにアクセスできます。
<i>ファイルは、リクエストしているアプリケーションのメッセージ範囲内にある場合のみアクセス可能です。</i>
### パスパラメータ
- `file_id` (string) 必須
プレビューするファイルの一意識別子。ファイルアップロード API レスポンスから取得します。
### クエリパラメータ
- `as_attachment` (boolean) オプション
ファイルを添付ファイルとして強制ダウンロードするかどうか。デフォルトは `false`(ブラウザでプレビュー)。
### レスポンス
ブラウザ表示またはダウンロード用の適切なヘッダー付きでファイル内容を返します。
- `Content-Type` ファイル MIME タイプに基づいて設定
- `Content-Length` ファイルサイズ(バイト、利用可能な場合)
- `Content-Disposition` `as_attachment=true` の場合は "attachment" に設定
- `Cache-Control` パフォーマンス向上のためのキャッシュヘッダー
- `Accept-Ranges` 音声/動画ファイルの場合は "bytes" に設定
### エラー
- 400, `invalid_param`, パラメータ入力異常
- 403, `file_access_denied`, ファイルアクセス拒否またはファイルが現在のアプリケーションに属していません
- 404, `file_not_found`, ファイルが見つからないか削除されています
- 500, サーバー内部エラー
</Col>
<Col sticky>
### リクエスト例
<CodeGroup title="Request" tag="GET" label="/files/:file_id/preview" targetCode={`curl -X GET '${props.appDetail.api_base_url}/files/72fa9618-8f89-4a37-9b33-7e1178a24a67/preview' \\\n--header 'Authorization: Bearer {api_key}'`}>
```bash {{ title: 'cURL' }}
curl -X GET '${props.appDetail.api_base_url}/files/72fa9618-8f89-4a37-9b33-7e1178a24a67/preview' \
--header 'Authorization: Bearer {api_key}'
```
</CodeGroup>
### 添付ファイルとしてダウンロード
<CodeGroup title="Request" tag="GET" label="/files/:file_id/preview" targetCode={`curl -X GET '${props.appDetail.api_base_url}/files/72fa9618-8f89-4a37-9b33-7e1178a24a67/preview?as_attachment=true' \\\n--header 'Authorization: Bearer {api_key}' \\\n--output downloaded_file.png`}>
```bash {{ title: 'cURL' }}
curl -X GET '${props.appDetail.api_base_url}/files/72fa9618-8f89-4a37-9b33-7e1178a24a67/preview?as_attachment=true' \
--header 'Authorization: Bearer {api_key}' \
--output downloaded_file.png
```
</CodeGroup>
### レスポンスヘッダー例
<CodeGroup title="Response Headers">
```http {{ title: 'ヘッダー - 画像プレビュー' }}
Content-Type: image/png
Content-Length: 1024
Cache-Control: public, max-age=3600
```
</CodeGroup>
### ファイルダウンロードレスポンスヘッダー
<CodeGroup title="Response Headers">
```http {{ title: 'ヘッダー - ファイルダウンロード' }}
Content-Type: image/png
Content-Length: 1024
Content-Disposition: attachment; filename*=UTF-8''example.png
Cache-Control: public, max-age=3600
```
</CodeGroup>
</Col>
</Row>
---
<Heading
url='/completion-messages/:task_id/stop'
method='POST'

View File

@@ -252,6 +252,86 @@ import { Row, Col, Properties, Property, Heading, SubProperty } from '../md.tsx'
</Col>
</Row>
---
<Heading
url='/files/:file_id/preview'
method='GET'
title='文件预览'
name='#file-preview'
/>
<Row>
<Col>
预览或下载已上传的文件。此端点允许您访问先前通过文件上传 API 上传的文件。
<i>文件只能在属于请求应用程序的消息范围内访问。</i>
### 路径参数
- `file_id` (string) 必需
要预览的文件的唯一标识符,从文件上传 API 响应中获得。
### 查询参数
- `as_attachment` (boolean) 可选
是否强制将文件作为附件下载。默认为 `false`(在浏览器中预览)。
### 响应
返回带有适当浏览器显示或下载标头的文件内容。
- `Content-Type` 根据文件 MIME 类型设置
- `Content-Length` 文件大小(以字节为单位,如果可用)
- `Content-Disposition` 如果 `as_attachment=true` 则设置为 "attachment"
- `Cache-Control` 用于性能的缓存标头
- `Accept-Ranges` 对于音频/视频文件设置为 "bytes"
### 错误
- 400, `invalid_param`, 参数输入异常
- 403, `file_access_denied`, 文件访问被拒绝或文件不属于当前应用程序
- 404, `file_not_found`, 文件未找到或已被删除
- 500, 服务内部错误
</Col>
<Col sticky>
### 请求示例
<CodeGroup title="Request" tag="GET" label="/files/:file_id/preview" targetCode={`curl -X GET '${props.appDetail.api_base_url}/files/72fa9618-8f89-4a37-9b33-7e1178a24a67/preview' \\\n--header 'Authorization: Bearer {api_key}'`}>
```bash {{ title: 'cURL' }}
curl -X GET '${props.appDetail.api_base_url}/files/72fa9618-8f89-4a37-9b33-7e1178a24a67/preview' \
--header 'Authorization: Bearer {api_key}'
```
</CodeGroup>
### 作为附件下载
<CodeGroup title="下载请求" tag="GET" label="/files/:file_id/preview?as_attachment=true" targetCode={`curl -X GET '${props.appDetail.api_base_url}/files/72fa9618-8f89-4a37-9b33-7e1178a24a67/preview?as_attachment=true' \\\n--header 'Authorization: Bearer {api_key}' \\\n--output downloaded_file.png`}>
```bash {{ title: 'cURL' }}
curl -X GET '${props.appDetail.api_base_url}/files/72fa9618-8f89-4a37-9b33-7e1178a24a67/preview?as_attachment=true' \
--header 'Authorization: Bearer {api_key}' \
--output downloaded_file.png
```
</CodeGroup>
### 响应标头示例
<CodeGroup title="Response Headers">
```http {{ title: 'Headers - 图片预览' }}
Content-Type: image/png
Content-Length: 1024
Cache-Control: public, max-age=3600
```
</CodeGroup>
### 文件下载响应标头
<CodeGroup title="Download Response Headers">
```http {{ title: 'Headers - 文件下载' }}
Content-Type: image/png
Content-Length: 1024
Content-Disposition: attachment; filename*=UTF-8''example.png
Cache-Control: public, max-age=3600
```
</CodeGroup>
</Col>
</Row>
---
<Heading
url='/completion-messages/:task_id/stop'
method='POST'

View File

@@ -392,6 +392,85 @@ Chat applications support session persistence, allowing previous chat history to
</Row>
---
<Heading
url='/files/:file_id/preview'
method='GET'
title='File Preview'
name='#file-preview'
/>
<Row>
<Col>
Preview or download uploaded files. This endpoint allows you to access files that have been previously uploaded via the File Upload API.
<i>Files can only be accessed if they belong to messages within the requesting application.</i>
### Path Parameters
- `file_id` (string) Required
The unique identifier of the file to preview, obtained from the File Upload API response.
### Query Parameters
- `as_attachment` (boolean) Optional
Whether to force download the file as an attachment. Default is `false` (preview in browser).
### Response
Returns the file content with appropriate headers for browser display or download.
- `Content-Type` Set based on file mime type
- `Content-Length` File size in bytes (if available)
- `Content-Disposition` Set to "attachment" if `as_attachment=true`
- `Cache-Control` Caching headers for performance
- `Accept-Ranges` Set to "bytes" for audio/video files
### Errors
- 400, `invalid_param`, abnormal parameter input
- 403, `file_access_denied`, file access denied or file does not belong to current application
- 404, `file_not_found`, file not found or has been deleted
- 500, internal server error
</Col>
<Col sticky>
### Request Example
<CodeGroup title="Request" tag="GET" label="/files/:file_id/preview" targetCode={`curl -X GET '${props.appDetail.api_base_url}/files/72fa9618-8f89-4a37-9b33-7e1178a24a67/preview' \\\n--header 'Authorization: Bearer {api_key}'`}>
```bash {{ title: 'cURL' }}
curl -X GET '${props.appDetail.api_base_url}/files/72fa9618-8f89-4a37-9b33-7e1178a24a67/preview' \
--header 'Authorization: Bearer {api_key}'
```
</CodeGroup>
### Download as Attachment
<CodeGroup title="Download Request" tag="GET" label="/files/:file_id/preview?as_attachment=true" targetCode={`curl -X GET '${props.appDetail.api_base_url}/files/72fa9618-8f89-4a37-9b33-7e1178a24a67/preview?as_attachment=true' \\\n--header 'Authorization: Bearer {api_key}' \\\n--output downloaded_file.png`}>
```bash {{ title: 'cURL' }}
curl -X GET '${props.appDetail.api_base_url}/files/72fa9618-8f89-4a37-9b33-7e1178a24a67/preview?as_attachment=true' \
--header 'Authorization: Bearer {api_key}' \
--output downloaded_file.png
```
</CodeGroup>
### Response Headers Example
<CodeGroup title="Response Headers">
```http {{ title: 'Headers - Image Preview' }}
Content-Type: image/png
Content-Length: 1024
Cache-Control: public, max-age=3600
```
</CodeGroup>
### Download Response Headers
<CodeGroup title="Download Response Headers">
```http {{ title: 'Headers - File Download' }}
Content-Type: image/png
Content-Length: 1024
Content-Disposition: attachment; filename*=UTF-8''example.png
Cache-Control: public, max-age=3600
```
</CodeGroup>
</Col>
</Row>
---
<Heading
url='/chat-messages/:task_id/stop'
method='POST'
@@ -653,7 +732,7 @@ Chat applications support session persistence, allowing previous chat history to
- `message_files` (array[object]) Message files
- `id` (string) ID
- `type` (string) File type, image for images
- `url` (string) Preview image URL
- `url` (string) File preview URL, use the File Preview API (`/files/{file_id}/preview`) to access the file
- `belongs_to` (string) belongs touser orassistant
- `answer` (string) Response message content
- `created_at` (timestamp) Creation timestamp, e.g., 1705395332

View File

@@ -654,7 +654,7 @@ import { Row, Col, Properties, Property, Heading, SubProperty, Paragraph } from
- `message_files` (array[object]) メッセージファイル
- `id` (string) ID
- `type` (string) ファイルタイプ、画像の場合はimage
- `url` (string) プレビュー画像URL
- `url` (string) ファイルプレビューURL、ファイルアクセスにはファイルプレビューAPI`/files/{file_id}/preview`)を使用してください
- `belongs_to` (string) 所属、userまたはassistant
- `answer` (string) 応答メッセージ内容
- `created_at` (timestamp) 作成タイムスタンプ、例1705395332
@@ -1422,6 +1422,86 @@ import { Row, Col, Properties, Property, Heading, SubProperty, Paragraph } from
</Row>
---
<Heading
url='/files/:file_id/preview'
method='GET'
title='ファイルプレビュー'
name='#file-preview'
/>
<Row>
<Col>
アップロードされたファイルをプレビューまたはダウンロードします。このエンドポイントを使用すると、以前にファイルアップロード API でアップロードされたファイルにアクセスできます。
<i>ファイルは、リクエストしているアプリケーションのメッセージ範囲内にある場合のみアクセス可能です。</i>
### パスパラメータ
- `file_id` (string) 必須
プレビューするファイルの一意識別子。ファイルアップロード API レスポンスから取得します。
### クエリパラメータ
- `as_attachment` (boolean) オプション
ファイルを添付ファイルとして強制ダウンロードするかどうか。デフォルトは `false`(ブラウザでプレビュー)。
### レスポンス
ブラウザ表示またはダウンロード用の適切なヘッダー付きでファイル内容を返します。
- `Content-Type` ファイル MIME タイプに基づいて設定
- `Content-Length` ファイルサイズ(バイト、利用可能な場合)
- `Content-Disposition` `as_attachment=true` の場合は "attachment" に設定
- `Cache-Control` パフォーマンス向上のためのキャッシュヘッダー
- `Accept-Ranges` 音声/動画ファイルの場合は "bytes" に設定
### エラー
- 400, `invalid_param`, パラメータ入力異常
- 403, `file_access_denied`, ファイルアクセス拒否またはファイルが現在のアプリケーションに属していません
- 404, `file_not_found`, ファイルが見つからないか削除されています
- 500, サーバー内部エラー
</Col>
<Col sticky>
### リクエスト例
<CodeGroup title="Request" tag="GET" label="/files/:file_id/preview" targetCode={`curl -X GET '${props.appDetail.api_base_url}/files/72fa9618-8f89-4a37-9b33-7e1178a24a67/preview' \\\n--header 'Authorization: Bearer {api_key}'`}>
```bash {{ title: 'cURL - ブラウザプレビュー' }}
curl -X GET '${props.appDetail.api_base_url}/files/72fa9618-8f89-4a37-9b33-7e1178a24a67/preview' \
--header 'Authorization: Bearer {api_key}'
```
</CodeGroup>
### 添付ファイルとしてダウンロード
<CodeGroup title="Download Request" tag="GET" label="/files/:file_id/preview?as_attachment=true" targetCode={`curl -X GET '${props.appDetail.api_base_url}/files/72fa9618-8f89-4a37-9b33-7e1178a24a67/preview?as_attachment=true' \\\n--header 'Authorization: Bearer {api_key}' \\\n--output downloaded_file.png`}>
```bash {{ title: 'cURL' }}
curl -X GET '${props.appDetail.api_base_url}/files/72fa9618-8f89-4a37-9b33-7e1178a24a67/preview?as_attachment=true' \
--header 'Authorization: Bearer {api_key}' \
--output downloaded_file.png
```
</CodeGroup>
### レスポンスヘッダー例
<CodeGroup title="Response Headers">
```http {{ title: 'ヘッダー - 画像プレビュー' }}
Content-Type: image/png
Content-Length: 1024
Cache-Control: public, max-age=3600
```
</CodeGroup>
### ダウンロードレスポンスヘッダー
<CodeGroup title="Download Response Headers">
```http {{ title: 'ヘッダー - ファイルダウンロード' }}
Content-Type: image/png
Content-Length: 1024
Content-Disposition: attachment; filename*=UTF-8''example.png
Cache-Control: public, max-age=3600
```
</CodeGroup>
</Col>
</Row>
---
<Heading
url='/meta'
method='GET'

View File

@@ -399,6 +399,86 @@ import { Row, Col, Properties, Property, Heading, SubProperty } from '../md.tsx'
</Col>
</Row>
---
<Heading
url='/files/:file_id/preview'
method='GET'
title='文件预览'
name='#file-preview'
/>
<Row>
<Col>
预览或下载已上传的文件。此端点允许您访问先前通过文件上传 API 上传的文件。
<i>文件只能在属于请求应用程序的消息范围内访问。</i>
### 路径参数
- `file_id` (string) 必需
要预览的文件的唯一标识符,从文件上传 API 响应中获得。
### 查询参数
- `as_attachment` (boolean) 可选
是否强制将文件作为附件下载。默认为 `false`(在浏览器中预览)。
### 响应
返回带有适当浏览器显示或下载标头的文件内容。
- `Content-Type` 根据文件 MIME 类型设置
- `Content-Length` 文件大小(以字节为单位,如果可用)
- `Content-Disposition` 如果 `as_attachment=true` 则设置为 "attachment"
- `Cache-Control` 用于性能的缓存标头
- `Accept-Ranges` 对于音频/视频文件设置为 "bytes"
### 错误
- 400, `invalid_param`, 参数输入异常
- 403, `file_access_denied`, 文件访问被拒绝或文件不属于当前应用程序
- 404, `file_not_found`, 文件未找到或已被删除
- 500, 服务内部错误
</Col>
<Col sticky>
### 请求示例
<CodeGroup title="Request" tag="GET" label="/files/:file_id/preview" targetCode={`curl -X GET '${props.appDetail.api_base_url}/files/72fa9618-8f89-4a37-9b33-7e1178a24a67/preview' \\\n--header 'Authorization: Bearer {api_key}'`}>
```bash {{ title: 'cURL' }}
curl -X GET '${props.appDetail.api_base_url}/files/72fa9618-8f89-4a37-9b33-7e1178a24a67/preview' \
--header 'Authorization: Bearer {api_key}'
```
</CodeGroup>
### 作为附件下载
<CodeGroup title="Request" tag="GET" label="/files/:file_id/preview?as_attachment=true" targetCode={`curl -X GET '${props.appDetail.api_base_url}/files/72fa9618-8f89-4a37-9b33-7e1178a24a67/preview?as_attachment=true' \\\n--header 'Authorization: Bearer {api_key}' \\\n--output downloaded_file.png`}>
```bash {{ title: 'cURL' }}
curl -X GET '${props.appDetail.api_base_url}/files/72fa9618-8f89-4a37-9b33-7e1178a24a67/preview?as_attachment=true' \
--header 'Authorization: Bearer {api_key}' \
--output downloaded_file.png
```
</CodeGroup>
### 响应标头示例
<CodeGroup title="Response Headers">
```http {{ title: 'Headers - 图片预览' }}
Content-Type: image/png
Content-Length: 1024
Cache-Control: public, max-age=3600
```
</CodeGroup>
### 文件下载响应标头
<CodeGroup title="Download Response Headers">
```http {{ title: 'Headers - 文件下载' }}
Content-Type: image/png
Content-Length: 1024
Content-Disposition: attachment; filename*=UTF-8''example.png
Cache-Control: public, max-age=3600
```
</CodeGroup>
</Col>
</Row>
---
<Heading
url='/chat-messages/:task_id/stop'
method='POST'
@@ -661,7 +741,7 @@ import { Row, Col, Properties, Property, Heading, SubProperty } from '../md.tsx'
- `message_files` (array[object]) 消息文件
- `id` (string) ID
- `type` (string) 文件类型image 图片
- `url` (string) 预览图片地址
- `url` (string) 文件预览地址,使用文件预览 API (`/files/{file_id}/preview`) 访问文件
- `belongs_to` (string) 文件归属方user 或 assistant
- `answer` (string) 回答消息内容
- `created_at` (timestamp) 创建时间

View File

@@ -356,6 +356,85 @@ Chat applications support session persistence, allowing previous chat history to
</Row>
---
<Heading
url='/files/:file_id/preview'
method='GET'
title='File Preview'
name='#file-preview'
/>
<Row>
<Col>
Preview or download uploaded files. This endpoint allows you to access files that have been previously uploaded via the File Upload API.
<i>Files can only be accessed if they belong to messages within the requesting application.</i>
### Path Parameters
- `file_id` (string) Required
The unique identifier of the file to preview, obtained from the File Upload API response.
### Query Parameters
- `as_attachment` (boolean) Optional
Whether to force download the file as an attachment. Default is `false` (preview in browser).
### Response
Returns the file content with appropriate headers for browser display or download.
- `Content-Type` Set based on file mime type
- `Content-Length` File size in bytes (if available)
- `Content-Disposition` Set to "attachment" if `as_attachment=true`
- `Cache-Control` Caching headers for performance
- `Accept-Ranges` Set to "bytes" for audio/video files
### Errors
- 400, `invalid_param`, abnormal parameter input
- 403, `file_access_denied`, file access denied or file does not belong to current application
- 404, `file_not_found`, file not found or has been deleted
- 500, internal server error
</Col>
<Col sticky>
### Request Example
<CodeGroup title="Request" tag="GET" label="/files/:file_id/preview" targetCode={`curl -X GET '${props.appDetail.api_base_url}/files/72fa9618-8f89-4a37-9b33-7e1178a24a67/preview' \\\n--header 'Authorization: Bearer {api_key}'`}>
```bash {{ title: 'cURL' }}
curl -X GET '${props.appDetail.api_base_url}/files/72fa9618-8f89-4a37-9b33-7e1178a24a67/preview' \
--header 'Authorization: Bearer {api_key}'
```
</CodeGroup>
### Download as Attachment
<CodeGroup title="Download Request" tag="GET" label="/files/:file_id/preview?as_attachment=true" targetCode={`curl -X GET '${props.appDetail.api_base_url}/files/72fa9618-8f89-4a37-9b33-7e1178a24a67/preview?as_attachment=true' \\\n--header 'Authorization: Bearer {api_key}' \\\n--output downloaded_file.png`}>
```bash {{ title: 'cURL' }}
curl -X GET '${props.appDetail.api_base_url}/files/72fa9618-8f89-4a37-9b33-7e1178a24a67/preview?as_attachment=true' \
--header 'Authorization: Bearer {api_key}' \
--output downloaded_file.png
```
</CodeGroup>
### Response Headers Example
<CodeGroup title="Response Headers">
```http {{ title: 'Headers - Image Preview' }}
Content-Type: image/png
Content-Length: 1024
Cache-Control: public, max-age=3600
```
</CodeGroup>
### Download Response Headers
<CodeGroup title="Download Response Headers">
```http {{ title: 'Headers - File Download' }}
Content-Type: image/png
Content-Length: 1024
Content-Disposition: attachment; filename*=UTF-8''example.png
Cache-Control: public, max-age=3600
```
</CodeGroup>
</Col>
</Row>
---
<Heading
url='/chat-messages/:task_id/stop'
method='POST'
@@ -617,7 +696,7 @@ Chat applications support session persistence, allowing previous chat history to
- `message_files` (array[object]) Message files
- `id` (string) ID
- `type` (string) File type, image for images
- `url` (string) Preview image URL
- `url` (string) File preview URL, use the File Preview API (`/files/{file_id}/preview`) to access the file
- `belongs_to` (string) belongs touser or assistant
- `agent_thoughts` (array[object]) Agent thoughtEmpty if it's a Basic Assistant
- `id` (string) Agent thought ID, every iteration has a unique agent thought ID

View File

@@ -356,6 +356,85 @@ import { Row, Col, Properties, Property, Heading, SubProperty, Paragraph } from
</Row>
---
<Heading
url='/files/:file_id/preview'
method='GET'
title='ファイルプレビュー'
name='#file-preview'
/>
<Row>
<Col>
アップロードされたファイルをプレビューまたはダウンロードします。このエンドポイントを使用すると、以前にファイルアップロード API でアップロードされたファイルにアクセスできます。
<i>ファイルは、リクエストしているアプリケーションのメッセージ範囲内にある場合のみアクセス可能です。</i>
### パスパラメータ
- `file_id` (string) 必須
プレビューするファイルの一意識別子。ファイルアップロード API レスポンスから取得します。
### クエリパラメータ
- `as_attachment` (boolean) オプション
ファイルを添付ファイルとして強制ダウンロードするかどうか。デフォルトは `false`(ブラウザでプレビュー)。
### レスポンス
ブラウザ表示またはダウンロード用の適切なヘッダー付きでファイル内容を返します。
- `Content-Type` ファイル MIME タイプに基づいて設定
- `Content-Length` ファイルサイズ(バイト、利用可能な場合)
- `Content-Disposition` `as_attachment=true` の場合は "attachment" に設定
- `Cache-Control` パフォーマンス向上のためのキャッシュヘッダー
- `Accept-Ranges` 音声/動画ファイルの場合は "bytes" に設定
### エラー
- 400, `invalid_param`, パラメータ入力異常
- 403, `file_access_denied`, ファイルアクセス拒否またはファイルが現在のアプリケーションに属していません
- 404, `file_not_found`, ファイルが見つからないか削除されています
- 500, サーバー内部エラー
</Col>
<Col sticky>
### リクエスト例
<CodeGroup title="Request" tag="GET" label="/files/:file_id/preview" targetCode={`curl -X GET '${props.appDetail.api_base_url}/files/72fa9618-8f89-4a37-9b33-7e1178a24a67/preview' \\\n--header 'Authorization: Bearer {api_key}'`}>
```bash {{ title: 'cURL' }}
curl -X GET '${props.appDetail.api_base_url}/files/72fa9618-8f89-4a37-9b33-7e1178a24a67/preview' \
--header 'Authorization: Bearer {api_key}'
```
</CodeGroup>
### 添付ファイルとしてダウンロード
<CodeGroup title="Download Request" tag="GET" label="/files/:file_id/preview?as_attachment=true" targetCode={`curl -X GET '${props.appDetail.api_base_url}/files/72fa9618-8f89-4a37-9b33-7e1178a24a67/preview?as_attachment=true' \\\n--header 'Authorization: Bearer {api_key}' \\\n--output downloaded_file.png`}>
```bash {{ title: 'cURL' }}
curl -X GET '${props.appDetail.api_base_url}/files/72fa9618-8f89-4a37-9b33-7e1178a24a67/preview?as_attachment=true' \
--header 'Authorization: Bearer {api_key}' \
--output downloaded_file.png
```
</CodeGroup>
### レスポンスヘッダー例
<CodeGroup title="Response Headers">
```http {{ title: 'Headers - 画像プレビュー' }}
Content-Type: image/png
Content-Length: 1024
Cache-Control: public, max-age=3600
```
</CodeGroup>
### ダウンロードレスポンスヘッダー
<CodeGroup title="Download Response Headers">
```http {{ title: 'Headers - ファイルダウンロード' }}
Content-Type: image/png
Content-Length: 1024
Content-Disposition: attachment; filename*=UTF-8''example.png
Cache-Control: public, max-age=3600
```
</CodeGroup>
</Col>
</Row>
---
<Heading
url='/chat-messages/:task_id/stop'
method='POST'
@@ -618,7 +697,7 @@ import { Row, Col, Properties, Property, Heading, SubProperty, Paragraph } from
- `message_files` (array[object]) メッセージファイル
- `id` (string) ID
- `type` (string) ファイルタイプ、画像の場合はimage
- `url` (string) プレビュー画像URL
- `url` (string) ファイルプレビューURL、ファイルアクセスにはファイルプレビューAPI`/files/{file_id}/preview`)を使用してください
- `belongs_to` (string) 所属、ユーザーまたはアシスタント
- `agent_thoughts` (array[object]) エージェントの思考(基本アシスタントの場合は空)
- `id` (string) エージェント思考ID、各反復には一意のエージェント思考IDがあります

View File

@@ -371,6 +371,86 @@ import { Row, Col, Properties, Property, Heading, SubProperty } from '../md.tsx'
</Col>
</Row>
---
<Heading
url='/files/:file_id/preview'
method='GET'
title='文件预览'
name='#file-preview'
/>
<Row>
<Col>
预览或下载已上传的文件。此端点允许您访问先前通过文件上传 API 上传的文件。
<i>文件只能在属于请求应用程序的消息范围内访问。</i>
### 路径参数
- `file_id` (string) 必需
要预览的文件的唯一标识符,从文件上传 API 响应中获得。
### 查询参数
- `as_attachment` (boolean) 可选
是否强制将文件作为附件下载。默认为 `false`(在浏览器中预览)。
### 响应
返回带有适当浏览器显示或下载标头的文件内容。
- `Content-Type` 根据文件 MIME 类型设置
- `Content-Length` 文件大小(以字节为单位,如果可用)
- `Content-Disposition` 如果 `as_attachment=true` 则设置为 "attachment"
- `Cache-Control` 用于性能的缓存标头
- `Accept-Ranges` 对于音频/视频文件设置为 "bytes"
### 错误
- 400, `invalid_param`, 参数输入异常
- 403, `file_access_denied`, 文件访问被拒绝或文件不属于当前应用程序
- 404, `file_not_found`, 文件未找到或已被删除
- 500, 服务内部错误
</Col>
<Col sticky>
### 请求示例
<CodeGroup title="Request" tag="GET" label="/files/:file_id/preview" targetCode={`curl -X GET '${props.appDetail.api_base_url}/files/72fa9618-8f89-4a37-9b33-7e1178a24a67/preview' \\\n--header 'Authorization: Bearer {api_key}'`}>
```bash {{ title: 'cURL' }}
curl -X GET '${props.appDetail.api_base_url}/files/72fa9618-8f89-4a37-9b33-7e1178a24a67/preview' \
--header 'Authorization: Bearer {api_key}'
```
</CodeGroup>
### 作为附件下载
<CodeGroup title="Request" tag="GET" label="/files/:file_id/preview?as_attachment=true" targetCode={`curl -X GET '${props.appDetail.api_base_url}/files/72fa9618-8f89-4a37-9b33-7e1178a24a67/preview?as_attachment=true' \\\\\\n--header 'Authorization: Bearer {api_key}' \\\\\\n--output downloaded_file.png`}>
```bash {{ title: 'cURL' }}
curl -X GET '${props.appDetail.api_base_url}/files/72fa9618-8f89-4a37-9b33-7e1178a24a67/preview?as_attachment=true' \
--header 'Authorization: Bearer {api_key}' \
--output downloaded_file.png
```
</CodeGroup>
### 响应标头示例
<CodeGroup title="Response Headers">
```http {{ title: 'Headers - 图片预览' }}
Content-Type: image/png
Content-Length: 1024
Cache-Control: public, max-age=3600
```
</CodeGroup>
### 文件下载响应标头
<CodeGroup title="Download Response Headers">
```http {{ title: 'Headers - 文件下载' }}
Content-Type: image/png
Content-Length: 1024
Content-Disposition: attachment; filename*=UTF-8''example.png
Cache-Control: public, max-age=3600
```
</CodeGroup>
</Col>
</Row>
---
<Heading
url='/chat-messages/:task_id/stop'
method='POST'
@@ -631,7 +711,7 @@ import { Row, Col, Properties, Property, Heading, SubProperty } from '../md.tsx'
- `message_files` (array[object]) 消息文件
- `id` (string) ID
- `type` (string) 文件类型image 图片
- `url` (string) 预览图片地址
- `url` (string) 文件预览地址,使用文件预览 API (`/files/{file_id}/preview`) 访问文件
- `belongs_to` (string) 文件归属方user 或 assistant
- `agent_thoughts` (array[object]) Agent思考内容仅Agent模式下不为空
- `id` (string) agent_thought ID每一轮Agent迭代都会有一个唯一的id

View File

@@ -747,6 +747,86 @@ Workflow applications offers non-session support and is ideal for translation, a
---
<Heading
url='/files/:file_id/preview'
method='GET'
title='File Preview'
name='#file-preview'
/>
<Row>
<Col>
Preview or download uploaded files. This endpoint allows you to access files that have been previously uploaded via the File Upload API.
<i>Files can only be accessed if they belong to messages within the requesting application.</i>
### Path Parameters
- `file_id` (string) Required
The unique identifier of the file to preview, obtained from the File Upload API response.
### Query Parameters
- `as_attachment` (boolean) Optional
Whether to force download the file as an attachment. Default is `false` (preview in browser).
### Response
Returns the file content with appropriate headers for browser display or download.
- `Content-Type` Set based on file mime type
- `Content-Length` File size in bytes (if available)
- `Content-Disposition` Set to "attachment" if `as_attachment=true`
- `Cache-Control` Caching headers for performance
- `Accept-Ranges` Set to "bytes" for audio/video files
### Errors
- 400, `invalid_param`, abnormal parameter input
- 403, `file_access_denied`, file access denied or file does not belong to current application
- 404, `file_not_found`, file not found or has been deleted
- 500, internal server error
</Col>
<Col sticky>
### Request Example
<CodeGroup title="Request" tag="GET" label="/files/:file_id/preview" targetCode={`curl -X GET '${props.appDetail.api_base_url}/files/72fa9618-8f89-4a37-9b33-7e1178a24a67/preview' \\\n--header 'Authorization: Bearer {api_key}'`}>
```bash {{ title: 'cURL' }}
curl -X GET '${props.appDetail.api_base_url}/files/72fa9618-8f89-4a37-9b33-7e1178a24a67/preview' \
--header 'Authorization: Bearer {api_key}'
```
</CodeGroup>
### Download as Attachment
<CodeGroup title="Download Request" tag="GET" label="/files/:file_id/preview?as_attachment=true" targetCode={`curl -X GET '${props.appDetail.api_base_url}/files/72fa9618-8f89-4a37-9b33-7e1178a24a67/preview?as_attachment=true' \\\n--header 'Authorization: Bearer {api_key}' \\\n--output downloaded_file.png`}>
```bash {{ title: 'cURL' }}
curl -X GET '${props.appDetail.api_base_url}/files/72fa9618-8f89-4a37-9b33-7e1178a24a67/preview?as_attachment=true' \
--header 'Authorization: Bearer {api_key}' \
--output downloaded_file.png
```
</CodeGroup>
### Response Headers Example
<CodeGroup title="Response Headers">
```http {{ title: 'Headers - Image Preview' }}
Content-Type: image/png
Content-Length: 1024
Cache-Control: public, max-age=3600
```
</CodeGroup>
### Download Response Headers
<CodeGroup title="Download Response Headers">
```http {{ title: 'Headers - File Download' }}
Content-Type: image/png
Content-Length: 1024
Content-Disposition: attachment; filename*=UTF-8''example.png
Cache-Control: public, max-age=3600
```
</CodeGroup>
</Col>
</Row>
---
<Heading
url='/workflows/logs'
method='GET'

View File

@@ -742,6 +742,86 @@ import { Row, Col, Properties, Property, Heading, SubProperty, Paragraph } from
---
<Heading
url='/files/:file_id/preview'
method='GET'
title='ファイルプレビュー'
name='#file-preview'
/>
<Row>
<Col>
アップロードされたファイルをプレビューまたはダウンロードします。このエンドポイントを使用すると、以前にファイルアップロード API でアップロードされたファイルにアクセスできます。
<i>ファイルは、リクエストしているアプリケーションのメッセージ範囲内にある場合のみアクセス可能です。</i>
### パスパラメータ
- `file_id` (string) 必須
プレビューするファイルの一意識別子。ファイルアップロード API レスポンスから取得します。
### クエリパラメータ
- `as_attachment` (boolean) オプション
ファイルを添付ファイルとして強制ダウンロードするかどうか。デフォルトは `false`(ブラウザでプレビュー)。
### レスポンス
ブラウザ表示またはダウンロード用の適切なヘッダー付きでファイル内容を返します。
- `Content-Type` ファイル MIME タイプに基づいて設定
- `Content-Length` ファイルサイズ(バイト、利用可能な場合)
- `Content-Disposition` `as_attachment=true` の場合は "attachment" に設定
- `Cache-Control` パフォーマンス向上のためのキャッシュヘッダー
- `Accept-Ranges` 音声/動画ファイルの場合は "bytes" に設定
### エラー
- 400, `invalid_param`, パラメータ入力異常
- 403, `file_access_denied`, ファイルアクセス拒否またはファイルが現在のアプリケーションに属していません
- 404, `file_not_found`, ファイルが見つからないか削除されています
- 500, サーバー内部エラー
</Col>
<Col sticky>
### リクエスト例
<CodeGroup title="Request" tag="GET" label="/files/:file_id/preview" targetCode={`curl -X GET '${props.appDetail.api_base_url}/files/72fa9618-8f89-4a37-9b33-7e1178a24a67/preview' \\\n--header 'Authorization: Bearer {api_key}'`}>
```bash {{ title: 'cURL - ブラウザプレビュー' }}
curl -X GET '${props.appDetail.api_base_url}/files/72fa9618-8f89-4a37-9b33-7e1178a24a67/preview' \
--header 'Authorization: Bearer {api_key}'
```
</CodeGroup>
### 添付ファイルとしてダウンロード
<CodeGroup title="Download Request" tag="GET" label="/files/:file_id/preview?as_attachment=true" targetCode={`curl -X GET '${props.appDetail.api_base_url}/files/72fa9618-8f89-4a37-9b33-7e1178a24a67/preview?as_attachment=true' \\\n--header 'Authorization: Bearer {api_key}' \\\n--output downloaded_file.png`}>
```bash {{ title: 'cURL' }}
curl -X GET '${props.appDetail.api_base_url}/files/72fa9618-8f89-4a37-9b33-7e1178a24a67/preview?as_attachment=true' \
--header 'Authorization: Bearer {api_key}' \
--output downloaded_file.png
```
</CodeGroup>
### レスポンスヘッダー例
<CodeGroup title="Response Headers">
```http {{ title: 'ヘッダー - 画像プレビュー' }}
Content-Type: image/png
Content-Length: 1024
Cache-Control: public, max-age=3600
```
</CodeGroup>
### ダウンロードレスポンスヘッダー
<CodeGroup title="Download Response Headers">
```http {{ title: 'ヘッダー - ファイルダウンロード' }}
Content-Type: image/png
Content-Length: 1024
Content-Disposition: attachment; filename*=UTF-8''example.png
Cache-Control: public, max-age=3600
```
</CodeGroup>
</Col>
</Row>
---
<Heading
url='/workflows/logs'
method='GET'

View File

@@ -730,6 +730,85 @@ Workflow 应用无会话支持,适合用于翻译/文章写作/总结 AI 等
</Row>
---
<Heading
url='/files/:file_id/preview'
method='GET'
title='文件预览'
name='#file-preview'
/>
<Row>
<Col>
预览或下载已上传的文件。此端点允许您访问先前通过文件上传 API 上传的文件。
<i>文件只能在属于请求应用程序的消息范围内访问。</i>
### 路径参数
- `file_id` (string) 必需
要预览的文件的唯一标识符,从文件上传 API 响应中获得。
### 查询参数
- `as_attachment` (boolean) 可选
是否强制将文件作为附件下载。默认为 `false`(在浏览器中预览)。
### 响应
返回带有适当浏览器显示或下载标头的文件内容。
- `Content-Type` 根据文件 MIME 类型设置
- `Content-Length` 文件大小(以字节为单位,如果可用)
- `Content-Disposition` 如果 `as_attachment=true` 则设置为 "attachment"
- `Cache-Control` 用于性能的缓存标头
- `Accept-Ranges` 对于音频/视频文件设置为 "bytes"
### 错误
- 400, `invalid_param`, 参数输入异常
- 403, `file_access_denied`, 文件访问被拒绝或文件不属于当前应用程序
- 404, `file_not_found`, 文件未找到或已被删除
- 500, 服务内部错误
</Col>
<Col sticky>
### 请求示例
<CodeGroup title="Request" tag="GET" label="/files/:file_id/preview" targetCode={`curl -X GET '${props.appDetail.api_base_url}/files/72fa9618-8f89-4a37-9b33-7e1178a24a67/preview' \\\n--header 'Authorization: Bearer {api_key}'`}>
```bash {{ title: 'cURL' }}
curl -X GET '${props.appDetail.api_base_url}/files/72fa9618-8f89-4a37-9b33-7e1178a24a67/preview' \
--header 'Authorization: Bearer {api_key}'
```
</CodeGroup>
### 作为附件下载
<CodeGroup title="Request" tag="GET" label="/files/:file_id/preview?as_attachment=true" targetCode={`curl -X GET '${props.appDetail.api_base_url}/files/72fa9618-8f89-4a37-9b33-7e1178a24a67/preview?as_attachment=true' \\\\\\n--header 'Authorization: Bearer {api_key}' \\\\\\n--output downloaded_file.png`}>
```bash {{ title: 'cURL' }}
curl -X GET '${props.appDetail.api_base_url}/files/72fa9618-8f89-4a37-9b33-7e1178a24a67/preview?as_attachment=true' \
--header 'Authorization: Bearer {api_key}' \
--output downloaded_file.png
```
</CodeGroup>
### 响应标头示例
<CodeGroup title="Response Headers">
```http {{ title: 'Headers - 图片预览' }}
Content-Type: image/png
Content-Length: 1024
Cache-Control: public, max-age=3600
```
</CodeGroup>
### 文件下载响应标头
<CodeGroup title="Download Response Headers">
```http {{ title: 'Headers - 文件下载' }}
Content-Type: image/png
Content-Length: 1024
Content-Disposition: attachment; filename*=UTF-8''example.png
Cache-Control: public, max-age=3600
```
</CodeGroup>
</Col>
</Row>
---
<Heading
url='/workflows/logs'
method='GET'