mcpField
F
file-storage

file-storage

github.com/tigrisdata云与 DevOps23.3k 次安装14 周前更新
代码仓库

关于 file-storage

这是一份使用 Tigris Object Storage 存储和提供文件的技能指南,涵盖 CLI 配置、`@tigrisdata/storage` SDK 操作及通过预签名 URL 实现浏览器上传。

主要功能

  1. 01配置 Tigris CLI 和凭据
  2. 02使用 SDK 上传和下载对象
  3. 03列出、查看和删除对象
  4. 04生成临时预签名 URL
  5. 05支持带进度的浏览器上传

适用场景

  1. 01为应用创建存储桶和访问密钥
  2. 02构建文件上传与下载流程
  3. 03让浏览器直传文件,避免服务器中转文件内容

安装

复制下面的命令,在终端中运行。

$npx skills add https://github.com/tigrisdata/skills --skill file-storage

安装后在 Claude Code 中输入/file-storage确认已加载。

包含的文件

除 SKILL.md 外,安装时一起下载的参考资料和脚本。

metadata.json
README.md
resources/
express.md
laravel.md
nextjs.md
rails.md
remix.md

详细介绍

Tigris 文件存储

使用 Tigris Object Storage 存储和提供文件。涵盖 CLI 设置(存储桶、访问密钥、身份验证)以及用于应用程序代码的 @tigrisdata/storage SDK。

快速开始

# 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
# .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
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" });

详细步骤请参阅下文的 使用 CLI 开始。


使用 CLI 开始

第 1 步:安装 CLI
npm install -g @tigrisdata/cli

验证安装:

tigris --version

t3 是 tigris 的别名——两者均可用于执行所有命令。

第 2 步:进行身份验证
tigris login

此操作会打开浏览器进行 OAuth 身份验证。登录后,使用以下命令进行验证:

tigris whoami

对于 CI/CD 或非交互式环境:

tigris configure --access-key <key> --access-secret <secret>
第 3 步:创建存储桶
tigris buckets create my-app-uploads

要点:

  • 存储桶默认是私有的。使用 --public 可让对象公开读取。
  • 存储桶默认是全局的。使用 --locations 可指定特定区域。
  • 在任意命令后输入 help 可查看其选项(例如,tigris buckets create help)。
第 4 步:创建访问密钥
tigris access-keys create "my-app-uploads-key"

此操作会输出 Access Key ID(tid_xxx)和 Secret Access Key(tsec_yyy)。

**Secret Access Key 只会显示一次。**请立即复制。Name 字段仅供人工识别,不会产生任何功能影响。

第 5 步:配置环境

在项目根目录中创建 .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

TIGRIS_STORAGE_BUCKET 用于为所有 SDK 调用设置默认存储桶。将 .env 添加到 .gitignore 中——切勿提交凭据。

第 6 步:为存储桶分配访问密钥
tigris access-keys assign tid_xxx --bucket my-app-uploads --role Editor

角色:

角色权限适用场景
Editor读取、写入和删除对象上传或删除文件的应用服务器
ReadOnly仅读取对象仅提供或下载文件的应用

现在你已经拥有:

  • 一个存储桶(my-app-uploads)
  • 一个访问密钥(tid_xxx / tsec_yyy)
  • 一个已分配给存储桶、具有 Editor 角色的密钥
  • 一个可供 SDK 使用的 .env 文件
第 7 步:安装 SDK
npm install @tigrisdata/storage
# or
yarn add @tigrisdata/storage

支持 ES Modules 和 CommonJS。


SDK 参考

所有方法均返回 TigrisStorageResponse<T, E>。务必先检查 error:

const result = await put("file.txt", "hello");
if (result.error) {
  console.error(result.error);
  return;
}
console.log(result.data);
config — 覆盖默认配置

每个方法都接受一个可选的 config 参数,其类型为 TigrisStorageConfig:

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

使用 config 可指定其他存储桶,或为每次调用使用不同的凭据:

// 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 — 上传
put(path: string, body: string | ReadableStream | Blob | Buffer, options?: PutOptions)
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 选项:

选项值默认值用途
accesspublic、privateprivate对象可见性
addRandomSuffixbooleanfalse为用户上传的文件添加随机后缀,避免同名文件冲突
allowOverwritebooleantrue允许替换现有文件
contentTypeMIME 字符串自动推断Content-Type 标头
contentDispositioninline、attachmentinline浏览器显示行为
multipartbooleanfalse启用大文件上传
onUploadProgress回调—{loaded, total, percentage}
configTigrisStorageConfig—覆盖存储桶或凭据(见上文的 config 部分)

响应数据:{ url, path, size, contentType, contentDisposition, modified }

get — 下载
get(path: string, format: "string" | "file" | "stream", options?: GetOptions)
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 选项:

选项值默认值用途
contentDispositioninline,attachmentinline显示或下载
contentTypeMIME 字符串上传时的值覆盖内容类型
encoding字符串utf-8文本编码
configTigrisStorageConfig—覆盖存储桶/凭据(参见上方的配置部分)
remove — 删除
remove(path: string, options?: RemoveOptions)
import { remove } from "@tigrisdata/storage";

const result = await remove("notes/hello.txt");
if (result.error) {
  console.error(result.error);
}
list — 列出对象
list(options?: ListOptions)
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 选项:

选项用途
prefix筛选以此字符串开头的键
delimiter对键分组(例如,用于文件夹的 "/")
limit每页的最大对象数(默认值:100)
paginationToken从上一页继续
configTigrisStorageConfig — 覆盖存储桶/凭据(参见上方的配置部分)

响应数据: { items, paginationToken, hasMore }

head — 对象元数据
head(path: string, options?: HeadOptions)
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 — 临时 URL
getPresignedUrl(path: string, options: GetPresignedUrlOptions)
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,
});

预签名 URL 选项:

选项值默认值用途
operationget,put—URL 用途
expiresIn秒3600过期时间
contentTypeMIME 字符串—PUT 必需
configTigrisStorageConfig—覆盖存储桶/凭据(参见上方的配置部分)

响应数据: { url, method, expiresIn }


客户端上传

无需通过服务器传输字节,即可直接从浏览器将文件上传到 Tigris。底层使用预签名 URL。

服务端 — 处理上传请求
// 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 },
    );
  }
}
客户端 — 直接上传
"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>}
    </>
  );
}

客户端上传选项:

选项必需用途
url是用于获取预签名 URL 的后端端点
access否public 或 private(默认值)
multipart否为大文件启用
partSize否每个分块的字节数(默认值:5 MiB)
concurrency否并行上传的分块数(默认值:4)
contentType否MIME 类型
onUploadProgress否{loaded, total, percentage}
React 组件(可选)

npm install @tigrisdata/react 提供可直接使用的 <Uploader> 组件,内置文件选择、进度显示和错误处理。有关用法,请参阅 @tigrisdata/react 文档。


关键规则

始终: 在读取 result.data 前检查 result.error | 默认将文件上传为 private — 仅在匿名用户需要直接访问 URL 时才设置 access: "public" | 浏览器上传时使用 handleClientUpload(不要通过服务器传输字节) | 对超过 100MB 的文件使用 multipart: true | 使用 hasMore + paginationToken 对 list() 进行分页 | 替换文件时删除旧文件(不会自动清理) | 在内容类型很重要时明确设置 contentType

切勿: 向客户端公开访问密钥(使用 handleClientUpload + @tigrisdata/storage/client 中的 upload()) | 跳过错误检查 | 使用 file.jpg 这类通用路径(使用 avatars/${userId}.jpg 或时间戳) | 忘记在创建时保存 Secret Access Key(它只显示一次)


已知问题

问题原因与解决方法
上传时出现 "Access denied"密钥未分配给存储桶。运行 tigris access-keys assign tid_xxx --bucket <name> --role Editor
SDK 返回 "Bucket not found".env 中的存储桶名称错误。使用 tigris buckets list 验证
Secret Access Key 丢失无法恢复。创建新密钥:tigris access-keys create "new-key",然后重新分配
文件无法公开访问存储桶默认是私有的。使用 --public 标志,或在 put() 中设置 access: "public"
大文件上传卡住对于超过 100MB 的文件,在 put 选项中添加 multipart: true
列表返回的结果不完整默认限制为 100。使用 hasMore 和 paginationToken 进行分页
客户端上传失败(CORS/500)服务端路由必须使用来自 @tigrisdata/storage 的 handleClientUpload

CLI 快速参考

t3 是 tigris 的别名。在任何命令后输入 help 可查看选项。

# 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

远程路径使用 t3:// 前缀:t3://my-bucket/path/file.txt


框架集成指南

如需了解特定框架的上传/下载模式,请阅读对应框架的资源文件:

框架SDK资源
Next.js@tigrisdata/storage(原生)阅读 ./resources/nextjs.md — Server Actions、API Routes、next/image、客户端上传
Remix@tigrisdata/storage(原生)阅读 ./resources/remix.md — action 函数、loaders、客户端上传
Express@tigrisdata/storage(原生)阅读 ./resources/express.md — Multer、流式上传、客户端上传
Railsaws-sdk-s3(暂时没有原生 Ruby SDK)阅读 ./resources/rails.md — Active Storage、直接上传、图像变体
Djangotigris-boto3-ext + django-storages使用 tigris-python-sdk 技能 — 涵盖 FileField、django-storages、预签名 URL
Laravelleague/flysystem-aws-s3-v3(暂时没有原生 PHP SDK)阅读 ./resources/laravel.md — Storage facade、Livewire 上传、预签名 URL
部署
框架平台设置环境变量的方式
Next.jsVercelDashboard → Settings → Environment Variables
RemixFly.iofly secrets set TIGRIS_STORAGE_ACCESS_KEY_ID=... ...
ExpressDocker使用 -e 标志或在 Compose 中配置 .env
RailsFly.io / Kamalfly secrets set 或 kamal env push
LaravelForge / VaporDashboard → Environment 或 vapor env:pull

相关技能

  • tigris-python-sdk — 通过 boto3 访问 Python、Django 上传、快照、分叉、Bundle API
  • tigris-bucket-management — 高级存储桶选项(区域、层级、快照)
  • tigris-object-operations — SDK 函数详细参考
  • tigris-snapshots-forking — 时间点恢复和存储桶分叉

官方文档

评论

支持 Markdown