Skip to content

File Operations Examples

Last Updated: 2025-11-23

Complete examples for file management including upload, download, listing, metadata, and file operations.


Table of Contents


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

curl -H "Authorization: Bearer ${ACCESS_TOKEN}" \
  http://localhost:8080/api/v1/files/file-789

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

curl -H "Authorization: Bearer ${ACCESS_TOKEN}" \
  http://localhost:8080/files/file-789/content

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):

{
  "error": "file not found"
}

Permission Denied (403 Forbidden):

{
  "error": "permission denied"
}

Invalid File (400 Bad Request):

{
  "error": "invalid file",
  "details": "file size exceeds maximum allowed"
}

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;
  }
}

See Also