Skip to content

S3 兼容性

使用 AWS SDK 在 IPFS Ninja 上上传、下载和管理文件,代码与 Amazon S3 完全相同。

Endpoint

https://s3.ipfs.ninja

凭证

S3 API 使用您的 IPFS Ninja API key 进行身份验证。您的 API key 同时充当 access key 和 secret key。

如何获取凭证

  1. 前往 Dashboard > API Keys
  2. 点击 Create API key 并设置名称(例如 "S3 access")
  3. 立即复制完整的 key — 它只会显示一次,之后无法找回

您的 key 格式如下:

bws_628bba35e9e0079d9ff9c392b1b55a7b
├──────────┘└──────────────────────────┘
 prefix (12 chars)    rest of key

映射到 AWS 凭证

AWS 参数示例
accessKeyIdAPI key 的前 12 个字符bws_628bba35
secretAccessKey完整的 API key(全部 36 个字符)bws_628bba35e9e0079d9ff9c392b1b55a7b
region始终为 us-east-1us-east-1

WARNING

完整的 API key 只在创建时显示一次。如果丢失,请删除该 key 并从 API Keys 页面 创建新的。

快速开始

javascript
import { S3Client, PutObjectCommand, GetObjectCommand } from "@aws-sdk/client-s3";

const s3 = new S3Client({
  endpoint: "https://s3.ipfs.ninja",
  credentials: {
    accessKeyId: "bws_628bba35",
    secretAccessKey: "bws_628bba35e9e0079d9ff9c392b1b55a7b"
  },
  region: "us-east-1",
  forcePathStyle: true
});

// Upload a file
const put = await s3.send(new PutObjectCommand({
  Bucket: "my-project",
  Key: "hello.json",
  Body: JSON.stringify({ hello: "IPFS" }),
  ContentType: "application/json"
}));

console.log("CID:", put.Metadata?.cid);
// CID: bafybeigdyrzt5sfp7udm7hu76uh7y26nf3efuylqabf3oclgtqy55fbzdi

Bucket = 文件夹

S3 bucket 对应您的 IPFS Ninja 文件夹。当您向 bucket 上传文件时,文件会存储在对应的文件夹中。当您列出 bucket 中的对象时,您看到的是该文件夹中的文件。

S3 操作IPFS Ninja 等效操作
CreateBucket创建新文件夹
ListBuckets列出您的文件夹
DeleteBucket删除文件夹及其中所有文件
PutObject 到 bucket上传文件到文件夹
ListObjectsV2 在 bucket 上列出文件夹中的文件
javascript
import { ListBucketsCommand, CreateBucketCommand, PutObjectCommand } from "@aws-sdk/client-s3";

// Create a bucket (= create a folder)
await s3.send(new CreateBucketCommand({ Bucket: "nft-metadata" }));

// Upload a file into the folder
await s3.send(new PutObjectCommand({
  Bucket: "nft-metadata",      // ← folder name
  Key: "token-42.json",        // ← filename within the folder
  Body: JSON.stringify({ name: "My NFT #42" })
}));

// List buckets (= list your folders)
const { Buckets } = await s3.send(new ListBucketsCommand({}));
console.log(Buckets);
// [{ Name: "nft-metadata", CreationDate: "2026-04-13T..." }]

TIP

通过 S3 API 创建的文件夹与您在 Dashboard 中看到的文件夹完全相同。您可以通过 S3 API、REST API 或 Web 界面来组织文件 — 它们共享同一个文件夹系统。

INFO

与 Amazon S3 不同,IPFS Ninja 的文件夹默认是扁平的。要创建嵌套结构,请使用 REST API 的文件夹 endpoint 配合 parentFolderId。在 S3 API 中,使用 key 前缀(例如 images/photo.png)在文件夹内进行组织。

Bucket 名称是全局唯一的

Bucket 名称位于所有客户共用的全局命名空间中,与 AWS S3 的语义一致。这意味着:

  • 第一个使用某个名称创建 bucket 的用户会在全局范围内占用该名称。
  • 之后任何账户使用相同名称调用 CreateBucket 都会返回 BucketAlreadyExists409)。
  • 如果您尝试重新创建自己已有的 bucket,会得到 BucketAlreadyOwnedByYou409)。
  • 您在 dashboard 中的文件夹名称是按账户区分的,仍然可以是任意名称 — 只有 S3 可见的 bucket 名称才受全局命名空间约束。

如果您想要的名称已被占用,请选择一个作用域更明确的名称(如 myapp-photos-2026acme-nft-metadata)— 与您在 Amazon S3 上采用的约定相同。

支持的操作

PutObject

将文件上传到 IPFS。文件会被固定、进行安全扫描,CID 通过 ETagx-amz-meta-cid header 返回。

如果要导入 CAR 文件 而非普通文件,请添加 x-amz-meta-import: car 元数据 header。详见 CAR 导入

javascript
import { PutObjectCommand } from "@aws-sdk/client-s3";
import fs from "fs";

const result = await s3.send(new PutObjectCommand({
  Bucket: "my-project",
  Key: "photo.png",
  Body: fs.readFileSync("photo.png"),
  ContentType: "image/png"
}));

console.log("CID:", result.ETag);
bash
# curl equivalent
curl -X PUT "https://s3.ipfs.ninja/my-project/photo.png" \
  --data-binary @photo.png \
  -H "Content-Type: image/png" \
  --aws-sigv4 "aws:amz:us-east-1:s3" \
  --user "bws_628bba35:bws_628bba35e9e0079d9ff9c392b1b55a7b"

GetObject

通过 key(文件名)或 CID 下载文件。

javascript
import { GetObjectCommand } from "@aws-sdk/client-s3";

const result = await s3.send(new GetObjectCommand({
  Bucket: "my-project",
  Key: "photo.png"
}));

const body = await result.Body.transformToByteArray();
console.log("Size:", body.length);
console.log("CID:", result.Metadata?.cid);

HeadObject

获取文件元数据而不下载内容。

javascript
import { HeadObjectCommand } from "@aws-sdk/client-s3";

const head = await s3.send(new HeadObjectCommand({
  Bucket: "my-project",
  Key: "photo.png"
}));

console.log("Size:", head.ContentLength);
console.log("Type:", head.ContentType);
console.log("CID:", head.Metadata?.cid);

DeleteObject

从 IPFS 取消固定文件并从您的帐户中删除。

javascript
import { DeleteObjectCommand } from "@aws-sdk/client-s3";

await s3.send(new DeleteObjectCommand({
  Bucket: "my-project",
  Key: "photo.png"
}));

ListObjectsV2

列出 bucket 中的文件,支持可选的前缀过滤和分页。

javascript
import { ListObjectsV2Command } from "@aws-sdk/client-s3";

const list = await s3.send(new ListObjectsV2Command({
  Bucket: "my-project",
  Prefix: "images/",
  MaxKeys: 100
}));

for (const obj of list.Contents ?? []) {
  console.log(obj.Key, obj.Size, obj.ETag); // ETag = CID
}

Multipart Upload

使用 multipart upload 上传大文件(最大 5 GB)。AWS SDK 会自动处理:

javascript
import { Upload } from "@aws-sdk/lib-storage";
import fs from "fs";

const upload = new Upload({
  client: s3,
  params: {
    Bucket: "my-project",
    Key: "large-dataset.tar.gz",
    Body: fs.createReadStream("large-dataset.tar.gz"),
    ContentType: "application/gzip"
  },
  partSize: 10 * 1024 * 1024, // 10 MB per part
});

upload.on("httpUploadProgress", (progress) => {
  console.log(`Uploaded ${progress.loaded} of ${progress.total} bytes`);
});

const result = await upload.done();
console.log("CID:", result.ETag);

或者手动控制分片:

javascript
import {
  CreateMultipartUploadCommand,
  UploadPartCommand,
  CompleteMultipartUploadCommand
} from "@aws-sdk/client-s3";

// 1. Start
const { UploadId } = await s3.send(new CreateMultipartUploadCommand({
  Bucket: "my-project",
  Key: "big-file.bin"
}));

// 2. Upload parts
const part1 = await s3.send(new UploadPartCommand({
  Bucket: "my-project",
  Key: "big-file.bin",
  UploadId,
  PartNumber: 1,
  Body: chunk1
}));

// 3. Complete
const result = await s3.send(new CompleteMultipartUploadCommand({
  Bucket: "my-project",
  Key: "big-file.bin",
  UploadId,
  MultipartUpload: {
    Parts: [{ PartNumber: 1, ETag: part1.ETag }]
  }
}));

Python 示例

python
import boto3

s3 = boto3.client(
    "s3",
    endpoint_url="https://s3.ipfs.ninja",
    aws_access_key_id="bws_628bba35",
    aws_secret_access_key="bws_628bba35e9e0079d9ff9c392b1b55a7b",
    region_name="us-east-1"
)

# Upload
s3.put_object(
    Bucket="my-project",
    Key="data.json",
    Body=b'{"hello": "IPFS"}',
    ContentType="application/json"
)

# List files
response = s3.list_objects_v2(Bucket="my-project")
for obj in response.get("Contents", []):
    print(obj["Key"], obj["Size"])

# Download
result = s3.get_object(Bucket="my-project", Key="data.json")
print(result["Body"].read())

Go 示例

go
package main

import (
    "context"
    "fmt"
    "strings"

    "github.com/aws/aws-sdk-go-v2/aws"
    "github.com/aws/aws-sdk-go-v2/credentials"
    "github.com/aws/aws-sdk-go-v2/service/s3"
)

func main() {
    client := s3.New(s3.Options{
        BaseEndpoint: aws.String("https://s3.ipfs.ninja"),
        Region:       "us-east-1",
        Credentials:  credentials.NewStaticCredentialsProvider("bws_628bba35", "bws_628bba35e9e0...", ""),
        UsePathStyle: true,
    })

    _, err := client.PutObject(context.TODO(), &s3.PutObjectInput{
        Bucket:      aws.String("my-project"),
        Key:         aws.String("hello.txt"),
        Body:        strings.NewReader("Hello, IPFS!"),
        ContentType: aws.String("text/plain"),
    })
    if err != nil {
        panic(err)
    }
    fmt.Println("Uploaded!")
}

配置 CORS(浏览器 SDK 访问)

如果您要直接从浏览器 JavaScript(SPA、钱包应用、dashboard 工具)调用 S3 API,需要先在 bucket 上配置 CORS。否则浏览器会阻止预检请求,导致上传失败并报 No 'Access-Control-Allow-Origin' header is present

与 AWS S3 的形式相同 — PutBucketCors / GetBucketCors / DeleteBucketCors 子资源:

PutBucketCors

javascript
import { S3Client, PutBucketCorsCommand } from "@aws-sdk/client-s3";

await s3.send(new PutBucketCorsCommand({
  Bucket: "my-bucket",
  CORSConfiguration: {
    CORSRules: [{
      AllowedOrigins: ["http://localhost:3000", "https://myapp.com"],
      AllowedMethods: ["GET", "HEAD", "PUT", "POST", "DELETE"],
      AllowedHeaders: ["*"],
      ExposeHeaders: ["ETag", "x-amz-meta-cid", "x-amz-request-id"],
      MaxAgeSeconds: 3600,
    }],
  },
}));

GetBucketCors

javascript
import { GetBucketCorsCommand } from "@aws-sdk/client-s3";

const { CORSRules } = await s3.send(new GetBucketCorsCommand({ Bucket: "my-bucket" }));
console.log(CORSRules);

DeleteBucketCors

javascript
import { DeleteBucketCorsCommand } from "@aws-sdk/client-s3";

await s3.send(new DeleteBucketCorsCommand({ Bucket: "my-bucket" }));

上限与默认值

  • 每个 bucket 最多 5 条规则(规范允许 100 条,我们限制为 5 条以保持浏览器预检响应体积紧凑)。
  • 配置序列化后总大小上限为 64 KB
  • 如果未设置 CORS 配置,浏览器预检请求会被拒绝 — 与 AWS S3 的默认策略一致。请为您实际提供服务的来源配置明确的规则。

关于安全性的实际说明

CORS 是浏览器端的便利层,不是安全边界。每个 S3 API 调用仍然需要由您的 API key 计算得出的有效 SigV4 签名 — 宽松的 CORS 配置并不会让任何人在没有该凭证的情况下使用您的 bucket。CORS 真正能防止的是:来自非预期来源(例如 dev.myapp.com 上您应用的过期副本)在浏览器环境中发送已签名的请求。

Dashboard 替代方式

Files 页面中每个文件夹的操作菜单里都有一个 S3 CORS 选项。底层存储相同;如果您不想编写 PutBucketCors 代码,可以在那里进行配置。

与 Amazon S3 的区别

特性Amazon S3IPFS Ninja S3
存储模型可变对象内容寻址(不可变 CID)
覆盖行为原地替换对象创建新 CID,旧 CID 仍可访问
版本控制支持不支持(使用 CID 进行版本管理)
服务端加密支持不支持(内容在 IPFS 上)
生命周期策略支持不支持
Bucket 策略 / ACL支持使用 gateway 访问模式
预签名 URL支持使用 签名上传 token
最大对象大小5 TB5 GB(multipart),100 MB(单次 PUT)
区域多区域us-east-1
ETagMD5 hashIPFS CID
额外 header标准 S3x-amz-meta-cid(IPFS CID)
CID 格式不适用新上传使用现代 CIDv1(bafy…);旧版 Qm… 仍可作为输入有效
Bucket 命名空间全局(AWS 范围内)全局(跨所有 IPFS Ninja 账户)— 语义相同
CORS支持 PutBucketCors支持 PutBucketCors(5 条规则上限)

从 Amazon S3 迁移

替换您的 S3 客户端配置:

diff
 const s3 = new S3Client({
+  endpoint: "https://s3.ipfs.ninja",
   credentials: {
-    accessKeyId: "AKIA...",
-    secretAccessKey: "wJalrX..."
+    accessKeyId: "bws_628bba35",
+    secretAccessKey: "bws_628bba35e9e0..."
   },
   region: "us-east-1",
+  forcePathStyle: true
 });

您现有的 PutObjectGetObjectListObjectsV2DeleteObject 调用无需修改即可使用。

从 Filebase 迁移

替换 endpoint URL:

diff
 const s3 = new S3Client({
-  endpoint: "https://s3.filebase.com",
+  endpoint: "https://s3.ipfs.ninja",
   credentials: {
-    accessKeyId: "FILEBASE_KEY",
-    secretAccessKey: "FILEBASE_SECRET"
+    accessKeyId: "bws_628bba35",
+    secretAccessKey: "bws_628bba35e9e0..."
   },
   region: "us-east-1",
   forcePathStyle: true
 });