File Operations Examples¶
Last Updated: 2025-11-23
Complete examples for file management including upload, download, listing, metadata, and file operations.
Table of Contents¶
- Upload Files
- Download Files
- List Files
- Get File Metadata
- Delete Files
- Move Files
- Copy Files
- Preview File Content
- Stream Media Files
File Operation Flows¶
File Upload Flow¶
The following diagram shows the complete file upload process including authentication, validation, storage, and metadata creation:
sequenceDiagram
participant Client
participant REST as REST API
participant AuthMW as Auth Middleware
participant FileHandler as File Handler
participant FileSvc as File Service
participant FileRepo as File Repository
participant Storage as File Storage
participant DB as SQLite
Client->>REST: POST /api/v1/files<br/>(multipart/form-data)
REST->>AuthMW: Authenticate request
AuthMW->>AuthMW: Validate JWT token
AuthMW->>AuthMW: Extract user & org ID
AuthMW->>FileHandler: Forward request
FileHandler->>FileHandler: Parse multipart form
FileHandler->>FileHandler: Extract file & metadata
FileHandler->>FileHandler: Validate file name
FileHandler->>FileHandler: Check file size
FileHandler->>FileSvc: Upload(ctx, file, metadata)
FileSvc->>FileSvc: Check permissions<br/>(file:write)
FileSvc->>FileSvc: Validate parent directory
FileSvc->>FileSvc: Generate file ID
FileSvc->>FileSvc: Calculate hash (SHA256)
FileSvc->>Storage: Save file content
Storage-->>FileSvc: Storage path
FileSvc->>FileRepo: Create(ctx, fileMetadata)
FileRepo->>FileRepo: Add org_id filter
FileRepo->>DB: INSERT INTO files
DB-->>FileRepo: File created
FileRepo-->>FileSvc: File record
FileSvc->>FileSvc: Increment metrics
FileSvc-->>FileHandler: File object
FileHandler->>FileHandler: Format response
FileHandler-->>REST: 201 Created
REST-->>Client: JSON response with file metadata
File Download Flow¶
sequenceDiagram
participant Client
participant REST as REST API
participant AuthMW as Auth Middleware
participant FileHandler as File Handler
participant FileSvc as File Service
participant FileRepo as File Repository
participant Storage as File Storage
participant DB as SQLite
Client->>REST: GET /api/v1/files/:id/download<br/>Range: bytes=0-1024 (optional)
REST->>AuthMW: Authenticate request
AuthMW->>AuthMW: Validate JWT token
AuthMW->>AuthMW: Extract user & org ID
AuthMW->>FileHandler: Forward request
FileHandler->>FileSvc: Download(ctx, fileID)
FileSvc->>FileSvc: Check permissions<br/>(file:read)
FileSvc->>FileRepo: GetByID(ctx, fileID)
FileRepo->>FileRepo: Add org_id filter
FileRepo->>DB: SELECT FROM files<br/>WHERE id = ? AND org_id = ?
DB-->>FileRepo: File metadata
FileRepo-->>FileSvc: File record
FileSvc->>FileSvc: Verify file exists
FileSvc->>Storage: Open file stream
Storage-->>FileSvc: File reader
FileSvc-->>FileHandler: File metadata + reader
FileHandler->>FileHandler: Handle Range request<br/>(HTTP 206 if Range header)
FileHandler->>FileHandler: Set headers<br/>(Content-Type, Content-Length)
FileHandler->>FileHandler: Stream file content
FileHandler-->>REST: 200 OK or 206 Partial
REST-->>Client: File stream
Note over Client,DB: For media files, HTTP Range<br/>requests enable seeking
File Deletion Flow¶
sequenceDiagram
participant Client
participant REST as REST API
participant AuthMW as Auth Middleware
participant RBACMW as RBAC Middleware
participant FileHandler as File Handler
participant FileSvc as File Service
participant FileRepo as File Repository
participant Storage as File Storage
participant DB as SQLite
Client->>REST: DELETE /api/v1/files/:id
REST->>AuthMW: Authenticate request
AuthMW->>AuthMW: Validate JWT token
AuthMW->>RBACMW: Forward request
RBACMW->>RBACMW: Check permission<br/>(file:delete)
RBACMW->>FileHandler: Forward request
FileHandler->>FileSvc: Delete(ctx, fileID)
FileSvc->>FileRepo: GetByID(ctx, fileID)
FileRepo->>DB: SELECT FROM files
DB-->>FileRepo: File metadata
FileRepo-->>FileSvc: File record
FileSvc->>FileSvc: Check ownership or<br/>admin permission
FileSvc->>FileSvc: Check if directory<br/>(must be empty)
FileSvc->>FileRepo: SoftDelete(ctx, fileID)
FileRepo->>DB: UPDATE files<br/>SET is_deleted = 1,<br/>deleted_at = NOW()
DB-->>FileRepo: Updated
FileRepo-->>FileSvc: Success
Note over FileSvc,Storage: File content remains<br/>for potential recovery
FileSvc->>FileSvc: Increment metrics
FileSvc-->>FileHandler: Success
FileHandler-->>REST: 204 No Content
REST-->>Client: Success
Upload Files¶
Upload a file to the system with optional parent directory.
REST API¶
Endpoint: POST /api/v1/files
Content-Type: multipart/form-data
curl Example¶
# Upload file to root
curl -X POST http://localhost:8080/api/v1/files \
-H "Authorization: Bearer ${ACCESS_TOKEN}" \
-F "file=@document.pdf"
# Upload file to specific directory
curl -X POST http://localhost:8080/api/v1/files \
-H "Authorization: Bearer ${ACCESS_TOKEN}" \
-F "file=@document.pdf" \
-F "parent_id=dir-123"
JavaScript/TypeScript Example¶
async function uploadFile(file: File, parentId?: string) {
const formData = new FormData();
formData.append('file', file);
if (parentId) {
formData.append('parent_id', parentId);
}
const response = await fetch('http://localhost:8080/api/v1/files', {
method: 'POST',
headers: {
'Authorization': `Bearer ${accessToken}`
},
body: formData
});
const uploadedFile = await response.json();
console.log('File uploaded:', uploadedFile.id);
return uploadedFile;
}
// Usage with file input
const input = document.querySelector('input[type="file"]');
input.addEventListener('change', async (e) => {
const file = e.target.files[0];
await uploadFile(file);
});
Python Example¶
import requests
def upload_file(file_path, access_token, parent_id=None):
with open(file_path, 'rb') as f:
files = {'file': f}
data = {}
if parent_id:
data['parent_id'] = parent_id
response = requests.post(
'http://localhost:8080/api/v1/files',
headers={'Authorization': f'Bearer {access_token}'},
files=files,
data=data
)
return response.json()
# Usage
uploaded_file = upload_file('document.pdf', access_token)
print(f"File uploaded: {uploaded_file['id']}")
Go Example¶
package main
import (
"bytes"
"io"
"mime/multipart"
"net/http"
"os"
"path/filepath"
)
func uploadFile(filePath, accessToken string, parentID *string) error {
file, err := os.Open(filePath)
if err != nil {
return err
}
defer file.Close()
body := &bytes.Buffer{}
writer := multipart.NewWriter(body)
// Add file
part, err := writer.CreateFormFile("file", filepath.Base(filePath))
if err != nil {
return err
}
io.Copy(part, file)
// Add parent ID if provided
if parentID != nil {
writer.WriteField("parent_id", *parentID)
}
writer.Close()
req, err := http.NewRequest("POST",
"http://localhost:8080/api/v1/files",
body)
if err != nil {
return err
}
req.Header.Set("Authorization", "Bearer "+accessToken)
req.Header.Set("Content-Type", writer.FormDataContentType())
client := &http.Client{}
resp, err := client.Do(req)
if err != nil {
return err
}
defer resp.Body.Close()
return nil
}
Response¶
{
"id": "file-789",
"name": "document.pdf",
"size": 1048576,
"mime_type": "application/pdf",
"parent_id": "dir-123",
"organization_id": "org-456",
"owner_id": "user-123",
"created_at": "2025-11-23T10:00:00Z",
"updated_at": "2025-11-23T10:00:00Z"
}
Download Files¶
Download file content with proper content-disposition headers.
REST API¶
Endpoint: GET /api/v1/files/:id/download
curl Example¶
# Download file
curl -H "Authorization: Bearer ${ACCESS_TOKEN}" \
http://localhost:8080/api/v1/files/file-789/download \
-o document.pdf
JavaScript/TypeScript Example¶
async function downloadFile(fileId: string, filename: string) {
const response = await fetch(
`http://localhost:8080/api/v1/files/${fileId}/download`,
{
headers: {
'Authorization': `Bearer ${accessToken}`
}
}
);
const blob = await response.blob();
// Create download link
const url = window.URL.createObjectURL(blob);
const a = document.createElement('a');
a.href = url;
a.download = filename;
document.body.appendChild(a);
a.click();
window.URL.revokeObjectURL(url);
document.body.removeChild(a);
}
// Usage
downloadFile('file-789', 'document.pdf');
Python Example¶
import requests
def download_file(file_id, access_token, output_path):
response = requests.get(
f'http://localhost:8080/api/v1/files/{file_id}/download',
headers={'Authorization': f'Bearer {access_token}'},
stream=True
)
with open(output_path, 'wb') as f:
for chunk in response.iter_content(chunk_size=8192):
f.write(chunk)
print(f'File downloaded to {output_path}')
# Usage
download_file('file-789', access_token, 'downloaded_document.pdf')
Go Example¶
func downloadFile(fileID, accessToken, outputPath string) error {
req, err := http.NewRequest("GET",
fmt.Sprintf("http://localhost:8080/api/v1/files/%s/download", fileID),
nil)
if err != nil {
return err
}
req.Header.Set("Authorization", "Bearer "+accessToken)
client := &http.Client{}
resp, err := client.Do(req)
if err != nil {
return err
}
defer resp.Body.Close()
out, err := os.Create(outputPath)
if err != nil {
return err
}
defer out.Close()
_, err = io.Copy(out, resp.Body)
return err
}
List Files¶
List files in a directory with pagination.
REST API¶
Endpoint: GET /api/v1/files
Query Parameters:
- parent_id (optional) - Directory ID to list files from
- limit (optional) - Maximum number of results (default: 100)
- offset (optional) - Pagination offset (default: 0)
curl Example¶
# List all files
curl -H "Authorization: Bearer ${ACCESS_TOKEN}" \
"http://localhost:8080/api/v1/files"
# List files in specific directory
curl -H "Authorization: Bearer ${ACCESS_TOKEN}" \
"http://localhost:8080/api/v1/files?parent_id=dir-123"
# List with pagination
curl -H "Authorization: Bearer ${ACCESS_TOKEN}" \
"http://localhost:8080/api/v1/files?parent_id=dir-123&limit=50&offset=100"
JavaScript/TypeScript Example¶
interface ListFilesOptions {
parentId?: string;
limit?: number;
offset?: number;
}
async function listFiles(options: ListFilesOptions = {}) {
const params = new URLSearchParams();
if (options.parentId) params.append('parent_id', options.parentId);
if (options.limit) params.append('limit', options.limit.toString());
if (options.offset) params.append('offset', options.offset.toString());
const response = await fetch(
`http://localhost:8080/api/v1/files?${params}`,
{
headers: {
'Authorization': `Bearer ${accessToken}`
}
}
);
return await response.json();
}
// Usage
const result = await listFiles({ parentId: 'dir-123', limit: 50 });
console.log(`Total files: ${result.total}`);
console.log(`Files:`, result.files);
Python Example¶
import requests
def list_files(access_token, parent_id=None, limit=100, offset=0):
params = {
'limit': limit,
'offset': offset
}
if parent_id:
params['parent_id'] = parent_id
response = requests.get(
'http://localhost:8080/api/v1/files',
headers={'Authorization': f'Bearer {access_token}'},
params=params
)
return response.json()
# Usage
result = list_files(access_token, parent_id='dir-123')
print(f"Total files: {result['total']}")
for file in result['files']:
print(f"- {file['name']} ({file['size']} bytes)")
Go Example¶
type ListFilesOptions struct {
ParentID *string
Limit int
Offset int
}
type ListFilesResponse struct {
Files []File `json:"files"`
Total int `json:"total"`
Limit int `json:"limit"`
Offset int `json:"offset"`
}
func listFiles(accessToken string, opts ListFilesOptions) (*ListFilesResponse, error) {
params := url.Values{}
if opts.ParentID != nil {
params.Add("parent_id", *opts.ParentID)
}
if opts.Limit > 0 {
params.Add("limit", strconv.Itoa(opts.Limit))
}
if opts.Offset > 0 {
params.Add("offset", strconv.Itoa(opts.Offset))
}
url := fmt.Sprintf("http://localhost:8080/api/v1/files?%s", params.Encode())
req, _ := http.NewRequest("GET", url, nil)
req.Header.Set("Authorization", "Bearer "+accessToken)
client := &http.Client{}
resp, err := client.Do(req)
if err != nil {
return nil, err
}
defer resp.Body.Close()
var result ListFilesResponse
json.NewDecoder(resp.Body).Decode(&result)
return &result, nil
}
Response¶
{
"files": [
{
"id": "file-789",
"name": "document.pdf",
"size": 1048576,
"mime_type": "application/pdf",
"parent_id": "dir-123",
"created_at": "2025-11-23T10:00:00Z"
},
{
"id": "file-790",
"name": "image.jpg",
"size": 524288,
"mime_type": "image/jpeg",
"parent_id": "dir-123",
"created_at": "2025-11-23T11:00:00Z"
}
],
"total": 42,
"limit": 100,
"offset": 0
}
Get File Metadata¶
Retrieve file metadata without downloading content.
REST API¶
Endpoint: GET /api/v1/files/:id
curl Example¶
JavaScript/TypeScript Example¶
async function getFileMetadata(fileId: string) {
const response = await fetch(
`http://localhost:8080/api/v1/files/${fileId}`,
{
headers: {
'Authorization': `Bearer ${accessToken}`
}
}
);
return await response.json();
}
// Usage
const file = await getFileMetadata('file-789');
console.log(`File: ${file.name}`);
console.log(`Size: ${file.size} bytes`);
console.log(`MIME Type: ${file.mime_type}`);
Python Example¶
import requests
def get_file_metadata(file_id, access_token):
response = requests.get(
f'http://localhost:8080/api/v1/files/{file_id}',
headers={'Authorization': f'Bearer {access_token}'}
)
return response.json()
# Usage
file = get_file_metadata('file-789', access_token)
print(f"File: {file['name']}")
print(f"Size: {file['size']} bytes")
Go Example¶
func getFileMetadata(fileID, accessToken string) (*File, error) {
req, _ := http.NewRequest("GET",
fmt.Sprintf("http://localhost:8080/api/v1/files/%s", fileID),
nil)
req.Header.Set("Authorization", "Bearer "+accessToken)
client := &http.Client{}
resp, err := client.Do(req)
if err != nil {
return nil, err
}
defer resp.Body.Close()
var file File
json.NewDecoder(resp.Body).Decode(&file)
return &file, nil
}
Delete Files¶
Permanently delete a file.
REST API¶
Endpoint: DELETE /api/v1/files/:id
curl Example¶
curl -X DELETE \
-H "Authorization: Bearer ${ACCESS_TOKEN}" \
http://localhost:8080/api/v1/files/file-789
JavaScript/TypeScript Example¶
async function deleteFile(fileId: string) {
const response = await fetch(
`http://localhost:8080/api/v1/files/${fileId}`,
{
method: 'DELETE',
headers: {
'Authorization': `Bearer ${accessToken}`
}
}
);
if (response.ok) {
console.log('File deleted successfully');
} else {
throw new Error('Failed to delete file');
}
}
// Usage
await deleteFile('file-789');
Python Example¶
import requests
def delete_file(file_id, access_token):
response = requests.delete(
f'http://localhost:8080/api/v1/files/{file_id}',
headers={'Authorization': f'Bearer {access_token}'}
)
if response.ok:
print('File deleted successfully')
else:
print('Failed to delete file')
# Usage
delete_file('file-789', access_token)
Go Example¶
func deleteFile(fileID, accessToken string) error {
req, _ := http.NewRequest("DELETE",
fmt.Sprintf("http://localhost:8080/api/v1/files/%s", fileID),
nil)
req.Header.Set("Authorization", "Bearer "+accessToken)
client := &http.Client{}
resp, err := client.Do(req)
if err != nil {
return err
}
defer resp.Body.Close()
if resp.StatusCode != http.StatusOK {
return fmt.Errorf("failed to delete file: %s", resp.Status)
}
return nil
}
Move Files¶
Move a file to a different directory.
REST API¶
Endpoint: PUT /api/v1/files/:id/move
curl Example¶
curl -X PUT http://localhost:8080/api/v1/files/file-789/move \
-H "Authorization: Bearer ${ACCESS_TOKEN}" \
-H "Content-Type: application/json" \
-d '{
"new_parent_id": "dir-456"
}'
JavaScript/TypeScript Example¶
async function moveFile(fileId: string, newParentId: string) {
const response = await fetch(
`http://localhost:8080/api/v1/files/${fileId}/move`,
{
method: 'PUT',
headers: {
'Authorization': `Bearer ${accessToken}`,
'Content-Type': 'application/json'
},
body: JSON.stringify({
new_parent_id: newParentId
})
}
);
return await response.json();
}
// Usage
const movedFile = await moveFile('file-789', 'dir-456');
console.log(`File moved to: ${movedFile.parent_id}`);
Python Example¶
import requests
def move_file(file_id, new_parent_id, access_token):
response = requests.put(
f'http://localhost:8080/api/v1/files/{file_id}/move',
headers={'Authorization': f'Bearer {access_token}'},
json={'new_parent_id': new_parent_id}
)
return response.json()
# Usage
moved_file = move_file('file-789', 'dir-456', access_token)
print(f"File moved to: {moved_file['parent_id']}")
Go Example¶
type MoveFileRequest struct {
NewParentID string `json:"new_parent_id"`
}
func moveFile(fileID, newParentID, accessToken string) (*File, error) {
reqData := MoveFileRequest{NewParentID: newParentID}
jsonData, _ := json.Marshal(reqData)
req, _ := http.NewRequest("PUT",
fmt.Sprintf("http://localhost:8080/api/v1/files/%s/move", fileID),
bytes.NewBuffer(jsonData))
req.Header.Set("Authorization", "Bearer "+accessToken)
req.Header.Set("Content-Type", "application/json")
client := &http.Client{}
resp, err := client.Do(req)
if err != nil {
return nil, err
}
defer resp.Body.Close()
var file File
json.NewDecoder(resp.Body).Decode(&file)
return &file, nil
}
Copy Files¶
Create a copy of a file.
REST API¶
Endpoint: POST /api/v1/files/:id/copy
curl Example¶
curl -X POST http://localhost:8080/api/v1/files/file-789/copy \
-H "Authorization: Bearer ${ACCESS_TOKEN}" \
-H "Content-Type: application/json" \
-d '{
"new_parent_id": "dir-456",
"new_name": "document_copy.pdf"
}'
JavaScript/TypeScript Example¶
async function copyFile(fileId: string, newParentId: string, newName?: string) {
const response = await fetch(
`http://localhost:8080/api/v1/files/${fileId}/copy`,
{
method: 'POST',
headers: {
'Authorization': `Bearer ${accessToken}`,
'Content-Type': 'application/json'
},
body: JSON.stringify({
new_parent_id: newParentId,
new_name: newName
})
}
);
return await response.json();
}
// Usage
const copiedFile = await copyFile('file-789', 'dir-456', 'document_copy.pdf');
console.log(`File copied with ID: ${copiedFile.id}`);
Python Example¶
import requests
def copy_file(file_id, new_parent_id, access_token, new_name=None):
data = {'new_parent_id': new_parent_id}
if new_name:
data['new_name'] = new_name
response = requests.post(
f'http://localhost:8080/api/v1/files/{file_id}/copy',
headers={'Authorization': f'Bearer {access_token}'},
json=data
)
return response.json()
# Usage
copied_file = copy_file('file-789', 'dir-456', access_token, 'document_copy.pdf')
print(f"File copied with ID: {copied_file['id']}")
Preview File Content¶
Get text or code file content for preview (supports syntax highlighting).
REST API¶
Endpoint: GET /files/:id/content
curl Example¶
JavaScript/TypeScript Example (with React)¶
import ReactMarkdown from 'react-markdown';
import { Prism as SyntaxHighlighter } from 'react-syntax-highlighter';
import { vscDarkPlus } from 'react-syntax-highlighter/dist/esm/styles/prism';
async function previewFile(fileId: string) {
const response = await fetch(
`http://localhost:8080/files/${fileId}/content`,
{
headers: {
'Authorization': `Bearer ${accessToken}`
}
}
);
return await response.json();
}
// React component for file preview
function FilePreview({ fileId }: { fileId: string }) {
const [preview, setPreview] = useState(null);
useEffect(() => {
previewFile(fileId).then(setPreview);
}, [fileId]);
if (!preview) return <div>Loading...</div>;
// Markdown preview
if (preview.content_type === 'markdown') {
return <ReactMarkdown>{preview.content}</ReactMarkdown>;
}
// Code preview with syntax highlighting
if (preview.content_type === 'code') {
return (
<SyntaxHighlighter language={preview.language} style={vscDarkPlus}>
{preview.content}
</SyntaxHighlighter>
);
}
// Plain text
return <pre>{preview.content}</pre>;
}
Stream Media Files¶
Stream video/audio files with HTTP Range Request support for seeking.
REST API¶
Endpoint: GET /api/v1/files/:id/stream
HTTP Range Requests: Supported for video/audio seeking
JavaScript/TypeScript Example (with React Player)¶
import ReactPlayer from 'react-player';
function VideoPlayer({ fileId }: { fileId: string }) {
const streamUrl = `http://localhost:8080/api/v1/files/${fileId}/stream`;
return (
<ReactPlayer
url={streamUrl}
controls
width="100%"
height="auto"
config={{
file: {
attributes: {
headers: {
'Authorization': `Bearer ${accessToken}`
}
}
}
}}
/>
);
}
HTML5 Video/Audio Example¶
<video controls>
<source
src="http://localhost:8080/api/v1/files/file-789/stream?token=${ACCESS_TOKEN}"
type="video/mp4"
/>
</video>
<audio controls>
<source
src="http://localhost:8080/api/v1/files/file-790/stream?token=${ACCESS_TOKEN}"
type="audio/mpeg"
/>
</audio>
Error Handling¶
Common Error Responses¶
File Not Found (404 Not Found):
Permission Denied (403 Forbidden):
Invalid File (400 Bad Request):
Example Error Handling¶
async function uploadFileWithErrorHandling(file: File) {
try {
const response = await fetch('http://localhost:8080/api/v1/files', {
method: 'POST',
headers: {
'Authorization': `Bearer ${accessToken}`
},
body: formData
});
if (!response.ok) {
const error = await response.json();
if (response.status === 400) {
throw new Error(`Invalid file: ${error.details || error.error}`);
} else if (response.status === 403) {
throw new Error('You do not have permission to upload files');
} else if (response.status === 413) {
throw new Error('File size exceeds maximum allowed size');
} else {
throw new Error('File upload failed');
}
}
return await response.json();
} catch (error) {
console.error('Upload error:', error);
throw error;
}
}