tigrisdata/skills

file-storage

Use when working with Tigris file storage - uploading, downloading, deleting, listing files, presigned URLs, client uploads, or setting up Tigris CLI and SDK.

소스 보기
원본 Skill 문서

원본 저장소의 제목, 예시, 코드, 표, 링크, 이미지를 유지해 표시합니다.

Tigris File Storage

Store and serve files with Tigris Object Storage. Covers CLI setup (bucket, access keys, auth) and the @tigrisdata/storage SDK for application code.

Quick Start

bash
# 1. Install CLI & authenticate
npm install -g @tigrisdata/cli
tigris login

# 2. Create bucket and access key
tigris buckets create my-app-uploads
tigris access-keys create "my-app-uploads-key"
# ⚠ Save the Secret Access Key — shown only once
tigris access-keys assign tid_xxx --bucket my-app-uploads --role Editor

# 3. Install SDK
npm install @tigrisdata/storage
bash
# .env
TIGRIS_STORAGE_ACCESS_KEY_ID=tid_xxx
TIGRIS_STORAGE_SECRET_ACCESS_KEY=tsec_yyy
TIGRIS_STORAGE_ENDPOINT=https://t3.storage.dev
TIGRIS_STORAGE_BUCKET=my-app-uploads
typescript
import { put } from "@tigrisdata/storage";

// Files are private by default — only authenticated requests can access them
const result = await put("avatars/user-123.jpg", file);
if (result.error) throw result.error;
console.log(result.data?.url);

// Use access: "public" only when anonymous users need direct URL access
// const result = await put("avatars/user-123.jpg", file, { access: "public" });

See Getting Started with CLI below for detailed steps.


Getting Started with CLI

Step 1: Install CLI

bash
npm install -g @tigrisdata/cli

Verify the installation:

bash
tigris --version

t3 is an alias for tigris — all commands work with either.

Step 2: Authenticate

bash
tigris login

Opens browser for OAuth. After login, verify with:

bash
tigris whoami

For CI/CD or non-interactive environments:

bash
tigris configure --access-key <key> --access-secret <secret>

Step 3: Create Bucket

bash
tigris buckets create my-app-uploads

Key points:

  • Buckets are private by default. Use --public for publicly readable objects.
  • Buckets are global by default. Use --locations to pin to specific regions.
  • Type help after any command to see its options (e.g., tigris buckets create help).

Step 4: Create Access Key

bash
tigris access-keys create "my-app-uploads-key"

This outputs an Access Key ID (tid_xxx) and Secret Access Key (tsec_yyy).

The Secret Access Key is only shown once. Copy it immediately. The Name field is only for human identification — it has no functional impact.

Step 5: Configure Environment

Create .env in your project root:

bash
TIGRIS_STORAGE_ACCESS_KEY_ID=tid_xxx
TIGRIS_STORAGE_SECRET_ACCESS_KEY=tsec_yyy
TIGRIS_STORAGE_ENDPOINT=https://t3.storage.dev
TIGRIS_STORAGE_BUCKET=my-app-uploads

TIGRIS_STORAGE_BUCKET sets the default bucket for all SDK calls. Add .env to .gitignore — never commit credentials.

Step 6: Assign Access Key to Bucket

bash
tigris access-keys assign tid_xxx --bucket my-app-uploads --role Editor

Roles:

RolePermissionsUse when
EditorRead + write + delete objectsApp servers that upload/delete files
ReadOnlyRead objects onlyApps that only serve/download files

Now you have:

  • A bucket (my-app-uploads)
  • An access key (tid_xxx / tsec_yyy)
  • The key assigned to the bucket with Editor role
  • A .env file ready for the SDK

Step 7: Install SDK

bash
npm install @tigrisdata/storage
# or
yarn add @tigrisdata/storage

Supports ES Modules and CommonJS.


SDK Reference

All methods return TigrisStorageResponse<T, E>. Always check error first:

typescript
const result = await put("file.txt", "hello");
if (result.error) {
  console.error(result.error);
  return;
}
console.log(result.data);

config — Override Default Configuration

Every method accepts an optional config parameter of type TigrisStorageConfig:

typescript
type TigrisStorageConfig = {
  bucket?: string;          // Override TIGRIS_STORAGE_BUCKET
  accessKeyId?: string;     // Override TIGRIS_STORAGE_ACCESS_KEY_ID
  secretAccessKey?: string; // Override TIGRIS_STORAGE_SECRET_ACCESS_KEY
  endpoint?: string;        // Override TIGRIS_STORAGE_ENDPOINT
};

Use config to target a different bucket or use different credentials per call:

typescript
// Upload to a different bucket
await put("report.pdf", data, { config: { bucket: "reports-archive" } });

// Use a separate read-only key for downloads
await get("file.txt", "string", { config: { accessKeyId: "tid_ro", secretAccessKey: "tsec_ro" } });

put — Upload

typescript
put(path: string, body: string | ReadableStream | Blob | Buffer, options?: PutOptions)
typescript
import { put } from "@tigrisdata/storage";

// Simple text upload
const result = await put("notes/hello.txt", "Hello, World!");

// Image with public access
const result = await put("avatars/user-123.jpg", file, {
  access: "public",
  contentType: "image/jpeg",
  addRandomSuffix: false,
});

// Large file with multipart and progress
const result = await put("videos/demo.mp4", fileStream, {
  multipart: true,
  onUploadProgress: ({ loaded, total, percentage }) => {
    console.log(`${loaded}/${total} bytes (${percentage}%)`);
  },
});

// Prevent accidental overwrite
const result = await put("config.json", data, {
  allowOverwrite: false,
});

Put options:

OptionValuesDefaultPurpose
accesspublic, privateprivateObject visibility
addRandomSuffixbooleanfalseAppend random suffix to avoid collisions on user-uploaded files with the same name
allowOverwritebooleantrueAllow replacing existing file
contentTypeMIME stringinferredContent type header
contentDispositioninline,attachmentinlineBrowser display behavior
multipartbooleanfalseEnable for large files
onUploadProgresscallback{loaded, total, percentage}
configTigrisStorageConfigOverride bucket/credentials (see config section above)

Response data: { url, path, size, contentType, contentDisposition, modified }

get — Download

typescript
get(path: string, format: "string" | "file" | "stream", options?: GetOptions)
typescript
import { get } from "@tigrisdata/storage";

// Read as string (text, JSON)
const result = await get("notes/hello.txt", "string");
console.log(result.data); // "Hello, World!"

// Serve as file (for API routes)
const result = await get("avatars/user-123.jpg", "file", {
  contentDisposition: "inline",
});

// Trigger browser download
const result = await get("reports/q4.pdf", "file", {
  contentDisposition: "attachment",
});

// Stream large files
const result = await get("videos/demo.mp4", "stream");

Get options:

OptionValuesDefaultPurpose
contentDispositioninline,attachmentinlineDisplay vs download
contentTypeMIME stringfrom uploadOverride content type
encodingstringutf-8Text encoding
configTigrisStorageConfigOverride bucket/credentials (see config section above)

remove — Delete

typescript
remove(path: string, options?: RemoveOptions)
typescript
import { remove } from "@tigrisdata/storage";

const result = await remove("notes/hello.txt");
if (result.error) {
  console.error(result.error);
}

list — List Objects

typescript
list(options?: ListOptions)
typescript
import { list } from "@tigrisdata/storage";

// List all objects
const result = await list();
console.log(result.data?.items);

// Filter by prefix
const result = await list({ prefix: "avatars/" });

// Paginate through all objects
const allFiles = [];
let page = await list({ limit: 100 });
allFiles.push(...(page.data?.items ?? []));

while (page.data?.hasMore) {
  page = await list({
    limit: 100,
    paginationToken: page.data.paginationToken,
  });
  allFiles.push(...(page.data?.items ?? []));
}

List options:

OptionPurpose
prefixFilter keys starting with this string
delimiterGroup keys (e.g., "/" for folders)
limitMax objects per page (default: 100)
paginationTokenContinue from previous page
configTigrisStorageConfig — override bucket/credentials (see config section above)

Response data: { items, paginationToken, hasMore }

head — Object Metadata

typescript
head(path: string, options?: HeadOptions)
typescript
import { head } from "@tigrisdata/storage";

const result = await head("avatars/user-123.jpg");
if (!result.error) {
  console.log(result.data);
  // { path, size, contentType, contentDisposition, modified, url }
}

getPresignedUrl — Temporary URLs

typescript
getPresignedUrl(path: string, options: GetPresignedUrlOptions)
typescript
import { getPresignedUrl } from "@tigrisdata/storage";

// Temporary download link (1 hour)
const result = await getPresignedUrl("reports/q4.pdf", {
  operation: "get",
  expiresIn: 3600,
});
console.log(result.data?.url);

// Temporary upload link (10 minutes)
const result = await getPresignedUrl("uploads/photo.jpg", {
  operation: "put",
  expiresIn: 600,
});

Presigned URL options:

OptionValuesDefaultPurpose
operationget,putURL purpose
expiresInseconds3600Expiration time
contentTypeMIME stringRequired for PUT
configTigrisStorageConfigOverride bucket/credentials (see config section above)

Response data: { url, method, expiresIn }


Client-Side Uploads

Upload files directly from the browser to Tigris without routing bytes through your server. Uses presigned URLs under the hood.

Server — Handle Upload Requests

typescript
// app/api/upload/route.ts
import { NextRequest, NextResponse } from "next/server";
import { handleClientUpload } from "@tigrisdata/storage";

export async function POST(request: NextRequest) {
  try {
    const body = await request.json();
    const { data, error } = await handleClientUpload(body);
    if (error) {
      return NextResponse.json({ error: error.message }, { status: 500 });
    }
    return NextResponse.json({ data });
  } catch (error) {
    return NextResponse.json(
      { error: "Failed to process upload request" },
      { status: 500 },
    );
  }
}

Client — Direct Upload

typescript
"use client";

import { upload } from "@tigrisdata/storage/client";
import { useState } from "react";

export default function FileUpload() {
  const [progress, setProgress] = useState(0);
  const [url, setUrl] = useState<string | null>(null);

  const handleFileChange = async (e: React.ChangeEvent<HTMLInputElement>) => {
    const file = e.target.files?.[0];
    if (!file) return;

    const result = await upload(file.name, file, {
      url: "/api/upload",
      access: "private",
      multipart: true,
      partSize: 10 * 1024 * 1024,
      onUploadProgress: ({ percentage }) => {
        setProgress(percentage);
      },
    });

    setUrl(result.url);
  };

  return (
    <>
      <input type="file" onChange={handleFileChange} />
      {progress > 0 && progress < 100 && <div>{progress}%</div>}
      {url && <div>Uploaded: {url}</div>}
    </>
  );
}

Client upload options:

OptionRequiredPurpose
urlYesBackend endpoint for presigned URLs
accessNopublic or private (default)
multipartNoEnable for large files
partSizeNoBytes per part (default: 5 MiB)
concurrencyNoParallel part uploads (default: 4)
contentTypeNoMIME type
onUploadProgressNo{loaded, total, percentage}

React Component (Optional)

npm install @tigrisdata/react provides a drop-in <Uploader> component with file selection, progress, and error handling built in. See @tigrisdata/react docs for usage.


Critical Rules

Always: Check result.error before result.data | Upload files as private by default — only set access: "public" when anonymous users need direct URL access | Use handleClientUpload for browser uploads (don't route bytes through server) | Use multipart: true for files over 100MB | Paginate list() with hasMore + paginationToken | Delete old files when replacing (no auto-cleanup) | Set contentType explicitly when it matters

Never: Expose access keys to the client (use handleClientUpload + upload() from @tigrisdata/storage/client) | Skip error checking | Use generic paths like file.jpg (use avatars/${userId}.jpg or timestamps) | Forget to save the Secret Access Key on creation (shown only once)


Known Issues

ProblemCause & Fix
"Access denied" on uploadKey not assigned to bucket. Run tigris access-keys assign tid_xxx --bucket <name> --role Editor
"Bucket not found" from SDKWrong bucket name in .env. Verify with tigris buckets list
Secret Access Key lostCannot recover. Create new: tigris access-keys create "new-key" and reassign
Files not publicly accessibleBucket is private by default. Use --public flag or access: "public" on put()
Upload hangs on large filesAdd multipart: true to put options for files over 100MB
List returns incomplete resultsDefault limit is 100. Use hasMore + paginationToken to paginate
Client upload fails (CORS/500)Server route must use handleClientUpload from @tigrisdata/storage

CLI Quick Reference

t3 is an alias for tigris. Type help after any command for options.

bash
# Auth
tigris login
tigris whoami

# Buckets
tigris buckets create <name> [--public] [--locations <region>]
tigris buckets list
tigris buckets delete <name>

# Access keys
tigris access-keys create "<name>"
tigris access-keys assign <tid_xxx> --bucket <name> --role Editor

# Objects
tigris cp <src> <dest> [-r]         # Upload/download/copy
tigris mv <src> <dest> [-rf]        # Move or rename
tigris rm <path> [-rf]              # Delete
tigris ls [bucket/prefix]           # List
tigris stat <path>                  # Metadata
tigris presign <path>               # Presigned URL
tigris touch <path>                 # Create empty object

Remote paths use t3:// prefix: t3://my-bucket/path/file.txt


Framework Integration Guides

For framework-specific upload/download patterns, read the resource file for your framework:

FrameworkSDKResource
Next.js@tigrisdata/storage (native)Read ./resources/nextjs.md — Server Actions, API Routes, next/image, client uploads
Remix@tigrisdata/storage (native)Read ./resources/remix.md — action functions, loaders, client uploads
Express@tigrisdata/storage (native)Read ./resources/express.md — Multer, streaming uploads, client uploads
Railsaws-sdk-s3 (no native Ruby SDK yet)Read ./resources/rails.md — Active Storage, direct uploads, image variants
Djangotigris-boto3-ext + django-storagesUse the tigris-python-sdk skill — covers FileField, django-storages, presigned URLs
Laravelleague/flysystem-aws-s3-v3 (no native PHP SDK yet)Read ./resources/laravel.md — Storage facade, Livewire uploads, presigned URLs

Deployment

FrameworkPlatformSet env vars with
Next.jsVercelDashboard → Settings → Environment Variables
RemixFly.iofly secrets set TIGRIS_STORAGE_ACCESS_KEY_ID=... ...
ExpressDocker-e flags or .env in Compose
RailsFly.io / Kamalfly secrets set or kamal env push
LaravelForge / VaporDashboard → Environment or vapor env:pull

Related Skills

  • tigris-python-sdk — Python access via boto3, Django uploads, snapshots, forks, Bundle API
  • tigris-bucket-management — Advanced bucket options (regions, tiers, snapshots)
  • tigris-object-operations — Detailed SDK function reference
  • tigris-snapshots-forking — Point-in-time recovery and bucket forking

Official Documentation

  • SDK: https://www.tigrisdata.com/docs/sdks/tigris/
  • Client Uploads: https://www.tigrisdata.com/docs/sdks/tigris/client-uploads/
  • Examples: https://www.tigrisdata.com/docs/sdks/tigris/examples/
같은 저장소의 Skills

더 많은 Skills

모든 Skills