Web3

2026年02月04日更新 18 人订阅
原价: ¥ 10 限时优惠
专栏简介

NFT 开发核心步骤:本地 IPFS 节点搭建与元数据上传实战

NFT开发核心步骤:本地IPFS节点搭建与元数据上传实战在Web3开发中,“将元数据上传到IPFS”是确保NFT资产去中心化的行业共识。然而,许多教程对此一笔带过,让开发者在面对环境配置、节点操作和脚本自动化时困难重重。从ipfsinit与daemon的区别,到实现图片和

NFT 开发核心步骤:本地 IPFS 节点搭建与元数据上传实战

在 Web3 开发中,“将元数据上传到 IPFS”是确保 NFT 资产去中心化的行业共识。然而,许多教程对此一笔带过,让开发者在面对环境配置、节点操作和脚本自动化时困难重重。从 ipfs initdaemon 的区别,到实现图片和 JSON 的链式上传,每个环节都可能成为项目的瓶颈。

本文旨在终结这种困惑。我们将以一篇“生产级”的实操指南,手把手带你走完在 macOS 上从零搭建本地 IPFS 节点,并使用 TypeScript 脚本自动化处理 NFT 元数据的完整流程。无论你是初涉此领域的开发者,还是希望规范化开发流程的资深工程师,都能在这里找到清晰、可复现的最佳实践。

告别 NFT 元数据上传的困惑!本篇实战指南将带你从零配置本地 IPFS 节点,到用 TypeScript 脚本实现图片与 JSON 的自动化链式上传,为你的 Web3 项目打下坚实基础。

实操

第一步:安装与配置

目标:让您的电脑拥有 ipfs 这个命令行工具,并让终端能找到它。

# 安装
brew install ipfs --cask
brew install --formula ipfs
# 修改 ~/.zshrc (让终端知道路径)
echo 'export PATH="/opt/homebrew/bin:$PATH"' >> ~/.zshrc
source ~/.zshrc
brew install ipfs
# brew link ipfs (创建“快捷方式”)
brew link ipfs

第二步:初始化节点

目标:在您的电脑上创建一个 IPFS 仓库(.ipfs 文件夹),生成您节点的唯一身份密钥和配置文件。这是一次性的操作。

ipfs init
generating ED25519 keypair...done
peer identity: 12D3KooWPvJRR6eakpAo4Kjb8jdPWJPhNpkV4ujTFfn4uxxPsgF2
initializing IPFS node at /Users/qiaopengjun/.ipfs

ipfs initipfs daemon 是 IPFS (InterPlanetary File System) 中两个不同的命令,它们的功能和用途有显著区别:

1. ipfs init

  • 作用:初始化一个新的 IPFS 节点(即本地仓库)。

  • 功能:

    • 在用户的主目录(默认是 ~/.ipfs)创建一个新的 IPFS 配置文件。
    • 生成一对加密密钥(用于节点身份验证)。
    • 创建默认的 IPFS 数据存储结构。
  • 使用场景:

    • 首次安装 IPFS 后,需要运行此命令来设置本地节点。
    • 只需运行一次(除非删除配置后重新初始化)。
  • 示例:

    ipfs init

2. ipfs daemon

  • 作用:启动 IPFS 守护进程(后台服务)。

  • 功能:

    • 启动一个长期运行的进程,使本地节点加入 IPFS 网络。
    • 启用以下功能:
    • 与其他节点通信(发现和连接对等节点)。
    • 提供本地 API 和网关服务(默认端口:50018080)。
    • 支持文件上传、下载和网络共享。
    • 持续运行直到手动终止(按 Ctrl+C 或关闭终端)。
  • 使用场景:

    • 需要与 IPFS 网络交互时(如上传/下载文件)。
    • 每次重启后或需要重新连接网络时运行。
  • 示例:

    ipfs daemon

关键区别

命令 目的 运行频率 结果
ipfs init 初始化本地节点配置 仅一次 创建 ~/.ipfs 目录和配置文件
ipfs daemon 启动节点并加入 IPFS 网络 每次需要使用时 激活网络连接和 API 服务

补充说明

  • 必须先运行 ipfs init 才能使用 ipfs daemon(否则会报错找不到配置)。
  • 如果 ipfs daemon 未运行,许多 IPFS 命令(如 ipfs addipfs cat)将无法正常工作。
  • 可以通过 ipfs config 命令修改初始化后的配置(如更改存储路径或端口)。

第三步:实现上传 IPFS 脚本


import { create } from "kubo-rpc-client";
import * as fs from "fs";
import { Buffer } from "buffer";
import * as path from "path";
import { fileURLToPath } from "url"; // ✅ 导入 url 模块的辅助函数

// ✅ 定义更详细的元数据接口,以匹配您的示例
interface Attribute {
  trait_type: string;
  value: string | number;
  display_type?: "number";
}

interface NftMetadata {
  name: string;
  description: string;
  image: string; // 将会是 ipfs://<Image_CID>
  external_url?: string;
  attributes: Attribute[];
}

// --- 配置 ---
const ipfs = create({ url: "http://localhost:5001/api/v0" });

// ✅ 新增:在 ESM 模块中获取当前目录路径的正确方法
const __filename = fileURLToPath(import.meta.url);
const __dirname = path.dirname(__filename);

/**
 * 上传一个本地文件到 IPFS。
 * @param filePath 文件的本地路径
 * @returns 上传结果或 undefined
 */
export async function uploadFileToIPFS(filePath: string) {
  try {
    console.log(`\n--- 正在上传文件: ${filePath} ---`);
    const file: Buffer = fs.readFileSync(filePath);

    const result = await ipfs.add({
      path: path.basename(filePath), // 只使用文件名
      content: file,
    });

    console.log("✅ 文件上传成功!");
    console.log(`   - 文件名: ${result.path}`);
    console.log(`   - CID: ${result.cid.toString()}`);
    console.log(`   - 大小: ${result.size} 字节`);
    return result;
  } catch (err) {
    console.error("❌ 上传文件失败:", err);
  }
}

/**
 * 将一个 JSON 对象上传到 IPFS。
 * @param json 要上传的 JSON 对象
 * @returns 上传结果或 undefined
 */
export async function uploadJSONToIPFS(json: NftMetadata) {
  try {
    console.log("\n--- 正在上传 JSON 对象 ---");
    const result = await ipfs.add(JSON.stringify(json));

    console.log("✅ JSON 元数据上传成功!");
    console.log(`   - CID: ${result.cid.toString()}`);
    console.log(`   - 大小: ${result.size} 字节`);
    return result;
  } catch (err) {
    console.error("❌ 上传 JSON 失败:", err);
  }
}

// 主执行函数
async function main() {
  try {
    // 检查 IPFS 节点连接
    const version = await ipfs.version();
    console.log(`✅ 成功连接到 IPFS 节点 (版本: ${version.version})`);

    // --- 步骤 1: 上传图片文件 ---
    // ✅ 修复:使用新的 __dirname 变量来构建正确的路径
    const imagePath = path.join(
      __dirname,
      "..",
      "..",
      "assets",
      "image",
      "IMG_20210626_1803...

剩余50%的内容订阅专栏后可查看

点赞 1
收藏 1
分享
本文参与登链社区写作激励计划 ,好文好收益,欢迎正在阅读的你也加入。

0 条评论

请先 登录 后评论