如何在 2026 年将文件上传到 IPFS — 完整指南

Learn how to upload JSON, images, PDFs and any file to IPFS using the dashboard, REST API, JavaScript, or Python. Step-by-step guide with code examples.

Nacho Coll作者: 更新时间: 18 分钟阅读
Learn how to upload JSON, images, PDFs and any file to IPFS using the dashboard, REST API, JavaScript, or Python. Step-by-step guide with code examples.

如果您曾经希望以一种没有任何单一公司可以控制、任何人都可以验证、并且抵抗审查的方式存储文件,那么星际文件系统(InterPlanetary File System,IPFS)就是答案。本指南将引导您了解将文件上传到 IPFS 的每一种实用方法——从简单的拖放仪表板到 JavaScript 和 Python 中可用于生产的代码。

带有拖放文件上传的 IPFS Ninja 上传页面

3 步上传您的第一个文件到 IPFS#

三步即可从零开始固定一个 CID:

  1. 创建 IPFS Ninja 账户。 前往 https://ipfs.ninja/signup 免费注册 —— 免费套餐提供 500 个文件和 1 GB 存储空间,无需信用卡。
  2. 生成 API 密钥。 在仪表板中打开 API Keys 部分并创建一个新密钥。每次请求都需要在 X-Api-Key 请求头中传递该密钥。
  3. 将文件 POST 到 /upload/newhttps://api.ipfs.ninja/upload/new 发送包含 content 字段的 JSON 请求体,响应会返回 CID 和网关 URL —— 文件已固定,可立即分享。

这是最快的路径。以下各节将详细介绍仪表板、curl、JavaScript 和 Python 版本的相同流程。

IPFS 内容寻址的工作原理#

IPFS 是一种用于存储和共享文件的对等协议。IPFS 不是按内容所在的位置(特定服务器上的 URL)来寻址内容,而是按内容是什么来寻址。每个文件都会获得一个独特的内容标识符(CID),该标识符源自其内容的加密哈希。这种设计为您提供了传统托管无法匹敌的三个特性:

  • 内容寻址。CID 保证您收到的数据正是上传的数据。如果有一个字节发生变化,CID 就会变化。任何人都无法在不使其地址失效的情况下篡改您的文件。
  • 永久性。只要网络上至少有一个节点固定(pin)了您的文件,它就会保持可用。像 IPFS Ninja 这样的固定服务可以确保您的文件保持在线,而无需您运行自己的基础设施。要了解为什么固定很重要以及垃圾回收的工作原理,请参阅什么是 IPFS 固定?
  • 抗审查。IPFS 上的文件不绑定到单个域名或服务器。没有政府或公司可以关闭的单点故障。

常见的用例包括 NFT 元数据和媒体、去中心化应用程序资产、科学数据集、公共记录,以及任何数据完整性至关重要的场景。

前提条件#

在上传任何内容之前,您需要两样东西:

  1. 一个 IPFS Ninja 账户。在 https://ipfs.ninja/signup 免费注册。免费计划为您提供 500 个文件和 1 GB 的存储空间——绰绰有余足以开始。

  2. 一个 API 密钥。登录后,进入仪表板中的 API Keys 部分并生成一个新密钥。它看起来像 bws_a1b2c3d4e5f6a7b8c9d0e1f2a3b4c5d6(前缀 bws_a1b2c3d4 显示在仪表板中以便识别)。请保密此密钥。您需要在每个 API 请求中将其传递到 X-Api-Key 标头中。

如果您只想使用 Web 仪表板,可以跳过 API 密钥步骤,但拥有 API 密钥可以让您从自己的应用程序中进行编程上传。

方法 1:Web 仪表板(拖放)#

上传文件最快的方法是通过 IPFS Ninja 仪表板。

  1. 导航到 https://ipfs.ninja/upload 并登录。
  2. 将任何文件——JSON、PNG、JPEG、PDF、SVG 或任何其他类型——拖放到上传区域,或点击浏览文件系统。
  3. 可选添加描述元数据(键值对)以帮助您稍后整理文件。
  4. 点击上传。几秒钟内,您的文件就会被固定到 IPFS,您将收到一个 CID 和一个网关 URL。

此方法支持任何文件类型,无需代码。它非常适合一次性上传、快速测试或需要固定内容的非技术团队成员。如果你需要一个包含错误处理和令牌刷新功能的现成 React 组件,请参照我们的 IPFS + React 教程

方法 2:使用 curl 的 REST API#

对于自动化和脚本,REST API 是最直接的方法。端点为:

POST https://api.ipfs.ninja/upload/new

每个请求都需要 X-Api-Key 标头和带有 content 字段的 JSON 主体。

上传 JSON#

要上传 JSON 对象,请直接将其作为 content 的值传递:

curl -X POST https://api.ipfs.ninja/upload/new \
  -H "Content-Type: application/json" \
  -H "X-Api-Key: bws_a1b2c3d4e5f6a7b8c9d0e1f2a3b4c5d6" \
  -d '{
    "content": {
      "name": "Astro Cat #42",
      "description": "A fearless cat exploring the cosmos.",
      "image": "ipfs://bafkreigh2akiscaildc7lqnpfuh3sksq4ogrtz7tpk2aulid5fhqotmqm",
      "attributes": [
        { "trait_type": "Background", "value": "Nebula" },
        { "trait_type": "Helmet", "value": "Gold" }
      ]
    },
    "description": "NFT metadata for Astro Cat #42",
    "metadata": { "collection": "astro-cats", "tokenId": "42" }
  }'

descriptionmetadata 字段是可选的。它们与您的文件一起存储在 IPFS Ninja 仪表板和 API 响应中,以帮助您稍后识别和过滤上传。它们不包含在固定到 IPFS 的内容中。

上传图片(二进制文件)#

对于图片或 PDF 等二进制文件,请对文件进行 base64 编码,并将编码后的字符串作为 content 传递:

curl -X POST https://api.ipfs.ninja/upload/new \
  -H "Content-Type: application/json" \
  -H "X-Api-Key: bws_a1b2c3d4e5f6a7b8c9d0e1f2a3b4c5d6" \
  -d "{
    \"content\": \"$(base64 -w 0 photo.png)\",
    \"description\": \"Product photo for landing page\",
    \"metadata\": { \"project\": \"website-redesign\" }
  }"

API 会自动检测内容类型。无需指定您要上传的是 JSON、图片还是 PDF——服务会为您处理。

方法 3:JavaScript / Node.js#

使用内置的 fetch API(Node.js 18+ 或任何现代浏览器),上传到 IPFS 只需几行代码。

上传 JSON#

const API_KEY = "bws_a1b2c3d4e5f6a7b8c9d0e1f2a3b4c5d6";

const metadata = {
  name: "Astro Cat #42",
  description: "A fearless cat exploring the cosmos.",
  image: "ipfs://bafkreigh2akiscaildc7lqnpfuh3sksq4ogrtz7tpk2aulid5fhqotmqm",
  attributes: [
    { trait_type: "Background", value: "Nebula" },
    { trait_type: "Helmet", value: "Gold" },
  ],
};

const response = await fetch("https://api.ipfs.ninja/upload/new", {
  method: "POST",
  headers: {
    "Content-Type": "application/json",
    "X-Api-Key": API_KEY,
  },
  body: JSON.stringify({
    content: metadata,
    description: "NFT metadata for Astro Cat #42",
  }),
});

const result = await response.json();
console.log("CID:", result.cid);
console.log("Gateway URL:", result.uris.url);

上传二进制文件#

import { readFileSync } from "node:fs";

const API_KEY = "bws_a1b2c3d4e5f6a7b8c9d0e1f2a3b4c5d6";
const fileBuffer = readFileSync("photo.png");
const base64Content = fileBuffer.toString("base64");

const response = await fetch("https://api.ipfs.ninja/upload/new", {
  method: "POST",
  headers: {
    "Content-Type": "application/json",
    "X-Api-Key": API_KEY,
  },
  body: JSON.stringify({
    content: base64Content,
    description: "Product photo",
    metadata: { project: "website-redesign" },
  }),
});

const result = await response.json();
console.log("CID:", result.cid);
console.log("IPFS URI:", result.uris.ipfs);
console.log("Gateway URL:", result.uris.url);

带错误处理的批量上传#

在生产环境中,您经常需要上传许多文件。这是一个带有重试的可重用助手:

import { readFileSync } from "node:fs";

const API_KEY = "bws_a1b2c3d4e5f6a7b8c9d0e1f2a3b4c5d6";
const API_URL = "https://api.ipfs.ninja/upload/new";

async function uploadToIPFS(content, options = {}) {
  const body = { content };
  if (options.description) body.description = options.description;
  if (options.metadata) body.metadata = options.metadata;

  const response = await fetch(API_URL, {
    method: "POST",
    headers: {
      "Content-Type": "application/json",
      "X-Api-Key": API_KEY,
    },
    body: JSON.stringify(body),
  });

  if (!response.ok) {
    const error = await response.text();
    throw new Error(`Upload failed (${response.status}): ${error}`);
  }

  return response.json();
}

// Upload JSON
const jsonResult = await uploadToIPFS(
  { name: "My Data", values: [1, 2, 3] },
  { description: "Sample dataset" }
);

// Upload image
const imageBase64 = readFileSync("banner.jpg").toString("base64");
const imageResult = await uploadToIPFS(imageBase64, {
  description: "Banner image",
  metadata: { format: "jpg", width: "1200" },
});

console.log("JSON CID:", jsonResult.cid);
console.log("Image CID:", imageResult.cid);

方法 4:Python#

Python 的 requests 库使 IPFS 上传变得简单。

上传 JSON#

import requests

API_KEY = "bws_a1b2c3d4e5f6a7b8c9d0e1f2a3b4c5d6"
API_URL = "https://api.ipfs.ninja/upload/new"

headers = {
    "Content-Type": "application/json",
    "X-Api-Key": API_KEY,
}

payload = {
    "content": {
        "name": "Astro Cat #42",
        "description": "A fearless cat exploring the cosmos.",
        "image": "ipfs://bafkreigh2akiscaildc7lqnpfuh3sksq4ogrtz7tpk2aulid5fhqotmqm",
        "attributes": [
            {"trait_type": "Background", "value": "Nebula"},
            {"trait_type": "Helmet", "value": "Gold"},
        ],
    },
    "description": "NFT metadata for Astro Cat #42",
    "metadata": {"collection": "astro-cats", "tokenId": "42"},
}

response = requests.post(API_URL, json=payload, headers=headers)
result = response.json()

print(f"CID: {result['cid']}")
print(f"Gateway URL: {result['uris']['url']}")

上传二进制文件#

import base64
import requests

API_KEY = "bws_a1b2c3d4e5f6a7b8c9d0e1f2a3b4c5d6"
API_URL = "https://api.ipfs.ninja/upload/new"

headers = {
    "Content-Type": "application/json",
    "X-Api-Key": API_KEY,
}

with open("photo.png", "rb") as f:
    encoded = base64.b64encode(f.read()).decode("utf-8")

payload = {
    "content": encoded,
    "description": "Product photo",
    "metadata": {"project": "website-redesign"},
}

response = requests.post(API_URL, json=payload, headers=headers)
result = response.json()

print(f"CID: {result['cid']}")
print(f"Size: {result['sizeMB']} MB")
print(f"IPFS URI: {result['uris']['ipfs']}")
print(f"Gateway URL: {result['uris']['url']}")

批量上传#

import base64
from pathlib import Path
import requests

API_KEY = "bws_a1b2c3d4e5f6a7b8c9d0e1f2a3b4c5d6"
API_URL = "https://api.ipfs.ninja/upload/new"
HEADERS = {"Content-Type": "application/json", "X-Api-Key": API_KEY}


def upload_file(file_path, description=None, metadata=None):
    with open(file_path, "rb") as f:
        encoded = base64.b64encode(f.read()).decode("utf-8")

    payload = {"content": encoded}
    if description:
        payload["description"] = description
    if metadata:
        payload["metadata"] = metadata

    response = requests.post(API_URL, json=payload, headers=HEADERS)
    response.raise_for_status()
    return response.json()


# Upload all PNGs in a directory
for png in Path("./images").glob("*.png"):
    result = upload_file(png, description=f"Image: {png.name}")
    print(f"{png.name} -> {result['cid']}")

理解响应#

每次成功上传都会返回一个 JSON 对象,其字段如下:

{
  "cid": "bafkreigh2akiscaildc7lqnpfuh3sksq4ogrtz7tpk2aulid5fhqotmqm",
  "sizeMB": 0.024,
  "uris": {
    "ipfs": "ipfs://bafkreigh2akiscaildc7lqnpfuh3sksq4ogrtz7tpk2aulid5fhqotmqm",
    "url": "https://ipfs.ninja/ipfs/bafkreigh2akiscaildc7lqnpfuh3sksq4ogrtz7tpk2aulid5fhqotmqm"
  }
}
字段描述
cid内容标识符——您文件内容的唯一哈希。这是您文件在 IPFS 上的永久地址。
sizeMB已上传文件的大小(以兆字节为单位)。
uris.ipfsIPFS 原生 URI(ipfs://CID)。从智能合约、NFT 元数据或其他 IPFS 感知应用程序引用文件时使用此项。
uris.url您可以在任何浏览器中打开的直接 HTTPS 网关 URL。非常适合共享、嵌入到网站或快速验证。

访问您的文件#

文件固定后,有两种主要的检索方式。

专用网关#

每个 IPFS Ninja 账户都包括一个带有自定义 slug 的专用网关:

https://your-slug.gw.ipfs.ninja/ipfs/{CID}

专用网关比公共网关更快、更可靠,因为它们直接连接到 IPFS Ninja 固定基础设施。您可以使用上传响应中的 CID 通过您的专用网关访问您的文件。

公共 IPFS 网关#

任何 IPFS 网关都可以使用 CID 提供您的内容:

https://ipfs.ninja/ipfs/{CID}     ← 您的专用网关
https://dweb.link/ipfs/{CID}       ← 公共网关
https://w3s.link/ipfs/{CID}        ← 公共网关

当您想要验证您的内容是否可以从 IPFS Ninja 网络外部访问时,公共网关非常有用。请记住,与您的专用网关相比,它们可能更慢或受到速率限制。

从智能合约和 dApp#

在链上存储引用时(例如在 ERC-721 令牌 URI 中),使用 ipfs:// URI:

ipfs://bafkreigh2akiscaildc7lqnpfuh3sksq4ogrtz7tpk2aulid5fhqotmqm

钱包、市场和其他 IPFS 感知应用程序知道如何通过任何可用的网关解析此 URI。

最佳实践#

将单个文件保持在 100 MB 以下。虽然 API 接受较大的上传,但较小的文件固定更快,并在 IPFS 网络上更有效地复制。如果您要处理大型数据集,请将它们拆分为较小的块。文件上传后,阅读 IPFS 性能优化 了解如何减少 gateway 延迟。

使用描述性元数据descriptionmetadata 字段是免费的,并且使您稍后更容易管理上传。包括项目名称、版本号或任何有助于您搜索和过滤的标识符。

在您自己的数据库中存储 CID。即使 IPFS Ninja 存储您的上传历史记录,维护您自己的 CID 到应用程序级标识符(用户 ID、订单号、令牌 ID)的映射也使您的集成更具弹性。

在链上引用中使用 IPFS URI。在智能合约或不可变记录中始终存储 ipfs://CID 而不是 HTTPS 网关 URL。网关 URL 可能会改变;CID 不会。

对于结构化数据,首选 JSON 内容。当您的数据是结构化的(元数据、配置、记录)时,请将其作为 JSON 对象传递,而不是将其编码为字符串。API 存储得更有效,并且生成的 IPFS 内容是人类可读的。

优雅地处理错误。检查 HTTP 状态码并对 5xx 响应实施带有指数退避的重试。API 为 4xx 响应(无效的 API 密钥、缺少内容、信用不足)返回明确的错误消息,您应该将这些消息呈现给用户。

结论#

将文件上传到 IPFS 从未如此简单。无论您喜欢可视化仪表板、快速 curl 命令,还是 JavaScript 或 Python 中的生产级集成,IPFS Ninja 都为您提供单一端点和跨所有内容类型的一致体验。

免费的 Dharma 计划包括 50 个文件、1 GB 存储空间和每月 2 GB 带宽——足以端到端构建和测试您的集成。当您准备扩展时,Bodhi 计划每月 5 美元、Karma 计划每月 19 美元和 Nirvana 计划每月 59 美元提供更高的限制、专用网关,以及(Nirvana 上的)优先支持。

创建您的免费账户 并在不到一分钟内开始将文件上传到 IPFS。

要深入了解带有签名令牌和客户端上传的 API,请参阅我们的 IPFS 上传 API 教程。有关包括列出文件、管理 API 密钥和配置网关的完整 API 参考,请访问文档。如果您正在从基于 S3 协议的固定服务迁移,可参考 Filebase 替代方案对比,了解两者在上传流程上的关键差异。

Python 开发者可以参考我们的 Python IPFS 上传指南 获取可直接使用的代码示例。

Nacho Coll

关于作者

Founder & Engineer at IPFS.NINJA

Nacho founded IPFS.NINJA to make content-addressed storage feel as simple as an S3 PUT — a single API call, a permanent CID, no wallets or peer discovery to reason about. Writes about IPFS internals, decentralized storage patterns, and the pinning-service landscape from the operator side of the wire.

返回博客

相关文章