2026 年 IPFS CID 完整指南:內容定址原理(含程式碼範例)

IPFS CID實戰指南:CIDv0與CIDv1比較、內容定址原理、以Node.js和Python產生CID、完整性驗證方法,以及2026年最新最佳實踐全面解析。

Nacho Coll作者: 更新時間: 18 分鐘閱讀
IPFS CID實戰指南:CIDv0與CIDv1比較、內容定址原理、以Node.js和Python產生CID、完整性驗證方法,以及2026年最新最佳實踐全面解析。

如果你曾使用過 IPFS(InterPlanetary File System,星際檔案系統),你可能遇過像 QmYwAPJzv5CZsnA625s3Xf2nemtYgPpHdWEz79ojWnPbdGbafybeigdyrzt5sfp7udm7hu76uh7y26nf3efuylqabf3oclgtqy55fbzdi 的字串。這些不是隨機的亂碼——它們是內容識別碼(CIDs),IPFS 內容定址系統的骨幹。

理解 CIDs 對任何在 IPFS 上構建應用的人來說都至關重要,無論你是上傳檔案、構建去中心化應用,還是實作內容分發系統。本指南將分解你需要知道關於 IPFS CIDs 的一切、內容定址如何運作,以及如何在你的專案中開始使用它們。

IPFS Ninja

5行程式碼產生CID(Python)#

import hashlib, base58

def cidv0(content: bytes) -> str:
    sha256 = hashlib.sha256(content).digest()
    return base58.b58encode(b"\x12\x20" + sha256).decode()

print(cidv0(b"hello world"))
# QmaozNR7DZHQK1ZcU9p7QdrshMvXqWK6gpvBw3cWE69N3T

這個 CID 任何人都能重現——在地球上任何地方用相同的位元組重新執行同樣的程式碼,都會得到相同的字串。這正是內容定址的本質。

Node.js 等效實作:

import { sha256 } from 'multiformats/hashes/sha2';
import { CID } from 'multiformats/cid';

const bytes = new TextEncoder().encode('hello world');
const hash = await sha256.digest(bytes);
const cid = CID.create(1, 0x55, hash); // version 1, raw codec
console.log(cid.toString());
// bafkreif7l7mvyofhyo6fgs6mjf5gguvm5jcahzgvfb3o7t5lmhz3...

什麼是 IPFS CID?#

**內容識別碼(CID)**是代表 IPFS 上一段內容的唯一指紋。與指向某個位置的傳統 web URL(如 https://example.com/file.pdf)不同,CIDs 指向內容本身,不管它儲存在哪裡。

這樣想:

  • 基於位置的定址:「去主街 123 號要那本紅色的書」
  • 基於內容的定址:「找到 ISBN 為 978-0-123456-78-9 的書」(不管哪間圖書館有它)

CIDs 以類似方式運作——它們根據內容的密碼學雜湊識別內容,使內容不可變可驗證。如果檔案中只變更一個位元組,CID 就會完全改變。

為什麼內容定址很重要#

傳統 web 架構依賴基於位置的定址。當你訪問 https://example.com/image.jpg 時,你信任:

  1. 域名擁有者沒有更改內容
  2. 伺服器在線且可訪問
  3. 內容沒有被竄改

使用 CIDs 進行內容定址:

  1. 不可變性:CID 保證內容沒有改變
  2. 去中心化:可以從任何擁有它的 IPFS 節點檢索內容
  3. 驗證:你可以以密碼學方式驗證收到了正確的內容
  4. 效率:相同內容會自動去重

CID 的剖析#

讓我們分解一個典型的 CID 來理解它的組成部分:

bafybeigdyrzt5sfp7udm7hu76uh7y26nf3efuylqabf3oclgtqy55fbzdi
└─┬─┘└─────────────────┬─────────────────────────────────┘
  │                   │
  │                   └─ 內容雜湊(Base32 編碼)
  └─ Multibase 前綴(指示編碼)

一個 CID 包含多個資訊片段:

1. Multibase 前綴#

第一個字元指示 CID 的編碼方式:

  • Q = Base58 編碼(CIDv0)
  • b = Base32 編碼(CIDv1)
  • f = Base16/十六進制(CIDv1)
  • z = Base58(CIDv1)

2. CID 版本#

  • CIDv0:總是以 Qm 開頭,使用 SHA-256,限於 DAG-PB 編解碼器
  • CIDv1:更靈活,支援多種雜湊函數和編解碼器

3. Multicodec#

指定內容的結構化方式(DAG-PB、DAG-CBOR、原始位元組等)

4. Multihash#

內容的實際密碼學雜湊,包括:

  • 雜湊函數識別碼(通常為 SHA-256)
  • 雜湊長度
  • 雜湊摘要

CIDv0 vs CIDv1:理解差異#

IPFS 已演進通過兩個主要 CID 版本,每個都有獨特特性:

CIDv0:原始格式#

CIDv0 CIDs 總是以 Qm 開頭,看起來像:

QmYwAPJzv5CZsnA625s3Xf2nemtYgPpHdWEz79ojWnPbdG

特性:

  • 僅 Base58 編碼
  • 僅 SHA-256 雜湊函數
  • 僅 DAG-PB(Protobuf)編解碼器
  • 46 個字元長
  • 與所有 IPFS 實作向後相容

何時使用 CIDv0:

  • 與舊 IPFS 節點的最大相容性
  • 與期望 Qm 前綴的現有系統合作
  • 檔案儲存(最常見的用例)

CIDv1:現代標準#

CIDv1 CIDs 更靈活,可能看起來像:

bafybeigdyrzt5sfp7udm7hu76uh7y26nf3efuylqabf3oclgtqy55fbzdi  # Base32
zb2rhj7crUKTQYRGCRATFaQ6YFLTde2YzdqbbhAASkL9uRDXn      # Base58
f01551220d1e2c35...                                      # Base16

特性:

  • 多種編碼格式(Base32、Base58、Base16)
  • 支援不同雜湊函數(SHA-256、SHA-512、BLAKE2 等)
  • 多種編解碼器(Raw、DAG-CBOR、DAG-JSON 等)
  • 自描述格式
  • 使用 Base32 時不區分大小寫

何時使用 CIDv1:

  • 構建新應用
  • 需要不區分大小寫的識別碼
  • 處理結構化資料(JSON、CBOR)
  • 使用替代雜湊函數

CIDv1子網域閘道的優勢#

子網域閘道(<cid>.ipfs.gateway.tld)之所以只能搭配 CIDv1 運作,原因在於 DNS 標籤規範:主機名稱標籤必須全部小寫,而 Base58 編碼(CIDv0 使用)是區分大小寫的,無法安全地放進 DNS 標籤中。CIDv1 預設採用 Base32 編碼,Base32 字母表本身不區分大小寫,因此瀏覽器與各種 HTTP 用戶端都能可靠地將 CID 當作子網域的一部分處理,不會有大小寫正規化造成雜湊不符的風險。

這帶來實際好處:每個 CID 都能取得自己獨立的來源(origin),瀏覽器的同源政策因此能個別套用在每個 CID 上,而不是讓所有內容共用同一個閘道網域的來源。這對於瀏覽器中執行不受信任的 IPFS 內容(例如去中心化網站或使用者上傳的 HTML)尤其重要,因為它能有效防止跨內容的 cookie 竊取與 DOM 存取等問題。這也是多數現代釘選服務與閘道供應商預設輸出 CIDv1 的核心原因。

常見錯誤:使用CIDv0存取子網域閘道#

一個常見的除錯情境是:開發者拿著一個以 Qm 開頭的 CIDv0 值,直接組成 Qm....ipfs.gateway.tld 的子網域網址,結果收到 DNS 解析失敗或憑證錯誤,而不是預期的內容。原因正是上面提到的大小寫問題——Base58 字元集包含大小寫混合的字母,不符合 DNS 標籤只能小寫的限制,子網域閘道因此無法正確路由請求。

修正方式很簡單:先用 ipfs cid base32 <cidv0> 或等效的程式庫函式,把 CIDv0 轉換成 CIDv1(結果會以 bafy 開頭),再組成子網域網址。轉換是確定性的,不會改變底層內容或其驗證結果,只是換一種編碼表示同一份資料。如果你的應用需要同時支援使用者貼上的新舊格式 CID,建議在存取子網域閘道前,一律先執行這個正規化步驟。

版本之間的轉換#

你可以在版本之間轉換 CIDs,同時保持相同的內容引用:

// CIDv0
const cidv0 = "QmYwAPJzv5CZsnA625s3Xf2nemtYgPpHdWEz79ojWnPbdG";

// Convert to CIDv1 Base32
const cidv1 = "bafybeigdyrzt5sfp7udm7hu76uh7y26nf3efuylqabf3oclgtqy55fbzdi";

// Both reference the same content!

內容定址如何運作#

IPFS 中的內容定址遵循確定性過程,確保相同內容總是產生相同 CID:

1. 內容準備#

當你將內容新增到 IPFS 時,它首先會被分解:

  • 小檔案:儲存為單一區塊
  • 大檔案:分割成片段並組織在 Merkle DAG(有向無環圖)中
  • 目錄:表示為連結到檔案的 DAG 結構

2. 雜湊處理過程#

每一塊內容經歷:

  1. 序列化:內容根據其編解碼器格式化
  2. 雜湊:密碼學雜湊函數處理序列化資料
  3. Multihash 建立:雜湊被包裝為演算法和長度資訊
  4. CID 組裝:版本、編解碼器和 multihash 結合

3. Merkle DAG 結構#

IPFS 將內容組織在 Merkle DAG 中,其中:

  • 每個節點有一個 CID
  • 父節點透過 CID 引用子節點
  • 任何節點中的變更會在樹中向上傳播
  • 整個結構可以用密碼學方式驗證
Root CID: bafybeigdyrzt5sfp7udm7hu76uh7y26nf3efuylqabf3oclgtqy55fbzdi
├── file1.txt (QmHash1...)
├── file2.jpg (QmHash2...)
└── subdirectory/
    ├── file3.pdf (QmHash3...)
    └── file4.mp4 (QmHash4...)

實用範例:使用 CIDs#

讓我們探索如何使用 IPFS Ninja API 在實踐中使用 CIDs:

上傳內容並取得 CID#

// Upload a file and get its CID
const uploadFile = async (content, filename) => {
  const response = await fetch('https://api.ipfs.ninja/upload/new', {
    method: 'POST',
    headers: {
      'X-Api-Key': 'bws_1234567890abcdef1234567890abcdef12345678',
      'Content-Type': 'application/json'
    },
    body: JSON.stringify({
      content: btoa(content), // Base64 encode binary content
      description: `Upload of ${filename}`
    })
  });
  
  const result = await response.json();
  console.log('CID:', result.cid);
  console.log('IPFS URL:', result.uris.ipfs);
  console.log('HTTP URL:', result.uris.url);
  
  return result.cid;
};

// Example usage
uploadFile('Hello, IPFS!', 'greeting.txt');
// Returns: bafkreifjxz6zwqh27k5xnr5qfbx4w6n5vuwwwdcngguwjewzj2e3xxfgvi

透過 CID 釘選現有內容#

如果你已有 CID,你可以釘選它以確保可用性:

const pinByCID = async (cid) => {
  const response = await fetch('https://api.ipfs.ninja/pin', {
    method: 'POST',
    headers: {
      'X-Api-Key': 'bws_1234567890abcdef1234567890abcdef12345678',
      'Content-Type': 'application/json'
    },
    body: JSON.stringify({
      cid: cid,
      description: 'Pinned via API'
    })
  });
  
  const result = await response.json();
  console.log('Pinned CID:', result.cid);
  
  return result;
};

// Pin the "Hello World" of IPFS
pinByCID('QmWATWQ7fVPP2EFGu71UkfnqhYXDYH566qy47CnJDgvs8u');

透過 CID 存取內容#

一旦你有了 CID,你可以透過各種方法存取內容:

// Direct IPFS gateway access
const ipfsUrl = `https://ipfs.ninja/ipfs/${cid}`;

// Custom gateway (if configured)
const customGatewayUrl = `https://my-app.gw.ipfs.ninja/ipfs/${cid}`;

// Fetch content programmatically
const fetchContent = async (cid) => {
  const response = await fetch(`https://ipfs.ninja/ipfs/${cid}`);
  const content = await response.text();
  return content;
};

開發者的 CID 最佳實踐#

1. 永遠驗證 CIDs#

在你的應用中使用 CID 之前,驗證其格式:

const isValidCID = (cid) => {
  // Basic validation patterns
  const cidv0Pattern = /^Qm[1-9A-HJ-NP-Za-km-z]{44}$/;
  const cidv1Pattern = /^[bf][a-z2-7]{58}$/;
  
  return cidv0Pattern.test(cid) || cidv1Pattern.test(cid);
};

2. 處理兩種 CID 版本#

你的應用應該同時處理 CIDv0 和 CIDv1:

const normalizeCID = (cid) => {
  if (cid.startsWith('Qm')) {
    // CIDv0 - can convert to CIDv1 if needed
    return cid;
  } else if (cid.startsWith('b') || cid.startsWith('f') || cid.startsWith('z')) {
    // CIDv1
    return cid;
  } else {
    throw new Error('Invalid CID format');
  }
};

3. 快取 CID 映射#

如果你經常產生 CIDs,考慮快取:

const cidCache = new Map();

const getCachedCID = (content) => {
  const contentHash = btoa(content);
  
  if (cidCache.has(contentHash)) {
    return cidCache.get(contentHash);
  }
  
  // Upload and cache result
  return uploadFile(content).then(cid => {
    cidCache.set(contentHash, cid);
    return cid;
  });
};

4. 使用有意義的描述#

上傳內容時,包含描述性元資料:

const uploadWithMetadata = async (content, metadata) => {
  return fetch('https://api.ipfs.ninja/upload/new', {
    method: 'POST',
    headers: {
      'X-Api-Key': 'bws_1234567890abcdef1234567890abcdef12345678',
      'Content-Type': 'application/json'
    },
    body: JSON.stringify({
      content: btoa(content),
      description: metadata.name || 'Uploaded file',
      metadata: {
        filename: metadata.filename,
        contentType: metadata.contentType,
        uploadedAt: new Date().toISOString(),
        version: metadata.version || '1.0'
      }
    })
  });
};

常見 CID 用例#

1. 靜態網站部署#

將整個網站部署到 IPFS 並透過 CID 引用它們:

// Upload website directory structure
const deployWebsite = async (files) => {
  const uploads = await Promise.all(
    files.map(file => uploadFile(file.content, file.path))
  );
  
  // Root CID references entire site
  const rootCID = uploads.find(u => u.path === 'index.html').cid;
  console.log(`Website deployed: https://ipfs.ninja/ipfs/${rootCID}`);
  
  return rootCID;
};

要了解更多關於網站部署的資訊,查看我們關於如何將檔案上傳到 IPFS 的指南。

2. NFT 元資料儲存#

使用 CIDs 不可變地儲存 NFT 元資料:

const nftMetadata = {
  name: "My Awesome NFT",
  description: "A unique digital collectible",
  image: "ipfs://bafkreibc5sgo2plmjkq2tzmhrn54bk3crhnqekiy7u66fqvqm37pu2e5gw",
  attributes: [
    { trait_type: "Color", value: "Blue" },
    { trait_type: "Rarity", value: "Epic" }
  ]
};

const metadataCID = await uploadFile(
  JSON.stringify(nftMetadata, null, 2), 
  'metadata.json'
);

// Use in smart contract
console.log(`Token URI: ipfs://${metadataCID}`);

3. 內容分發#

使用 CIDs 進行分散式內容交付:

// Upload once, access everywhere
const distributeContent = async (content) => {
  const cid = await uploadFile(content, 'content.txt');
  
  // Content available via multiple gateways
  const gateways = [
    `https://ipfs.ninja/ipfs/${cid}`,
    `https://ipfs.io/ipfs/${cid}`,
    `https://cloudflare-ipfs.com/ipfs/${cid}`
  ];
  
  return { cid, gateways };
};

使用 CIDs 理解 IPFS 釘選#

CIDs 預設是臨時的——它們必須被「釘選」才能保持可用。在我們關於什麼是 IPFS 釘選 的完整指南中了解更多關於這個關鍵概念。

選擇 IPFS 釘選服務時,考慮閱讀我們的 IPFS Ninja vs Pinata 比較或探索我們對今天可用的最佳 IPFS 釘選服務 的綜述。

在 30 秒內建立你的第一個 CID#

準備好產生你的第一個 CID 了嗎?這是使用 IPFS Ninja API 的快速範例:

# Using curl (replace with your actual API key)
curl -X POST https://api.ipfs.ninja/upload/new \
  -H "X-Api-Key: bws_YOUR_API_KEY_HERE" \
  -H "Content-Type: application/json" \
  -d '{
    "content": "SGVsbG8sIElQRlMgV29ybGQh",
    "description": "My first IPFS upload"
  }'
// Using JavaScript
const createFirstCID = async () => {
  const response = await fetch('https://api.ipfs.ninja/upload/new', {
    method: 'POST',
    headers: {
      'X-Api-Key': 'bws_YOUR_API_KEY_HERE',
      'Content-Type': 'application/json'
    },
    body: JSON.stringify({
      content: btoa('Hello, IPFS World!'), // Base64: "SGVsbG8sIElQRlMgV29ybGQh"
      description: 'My first IPFS upload'
    })
  });
  
  const result = await response.json();
  console.log('🎉 Your first CID:', result.cid);
  console.log('🌐 Access it at:', result.uris.url);
  
  return result;
};

createFirstCID();

這將回傳類似:

{
  "cid": "bafkreif2pall7dybz7vecqka3zo24irdwabf7rbiiweuhau7a2hjlqvfjw",
  "sizeMB": 0.000017,
  "uris": {
    "ipfs": "ipfs://bafkreif2pall7dybz7vecqka3zo24irdwabf7rbiiweuhau7a2hjlqvfjw",
    "url": "https://ipfs.ninja/ipfs/bafkreif2pall7dybz7vecqka3zo24irdwabf7rbiiweuhau7a2hjlqvfjw"
  }
}

更詳細的 API 範例,請查看我們的 IPFS 上傳 API 教學

結論#

CIDs 是 IPFS 內容定址系統的基礎,提供不可變、可驗證和去中心化的內容識別。理解它們如何運作——從 CIDv0 vs CIDv1 的技術細節到實際的實作模式——對於構建強大的去中心化應用至關重要。

關鍵要點:

  • CIDs 唯一識別內容,而非位置
  • CIDv0 提供最大相容性,CIDv1 提供靈活性
  • 內容定址實現驗證和去重
  • 正確的 CID 處理對生產應用至關重要

無論你是儲存 NFT 元資料、部署去中心化網站,還是構建內容分發系統,CIDs 都提供你真正去中心化應用所需的可靠基礎。

準備好開始釘選了嗎? 建立免費帳戶 —— 50 個檔案、1 GB 儲存、2 GB 頻寬/月。無需信用卡。

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.

返回部落格

相關文章