使用ipfs.httpclient上传文件前需确保节点支持写操作:本地节点要加--writable参数,infura需开启write api并带secret认证;multipart表单字段名必须为file;大文件需设超时、分块及响应头读取策略;上传后须pin固定且通过网关访问。

用 IPFS.HttpClient 上传文件前必须确认节点可写
IPFS 不是“传完就自动公开”的网盘,你调用的节点(比如本地 ipfs daemon 或 Infura 的 endpoint)得支持 /api/v0/add 写操作。Infura 免费 tier 默认禁用上传,会返回 403 Forbidden;本地节点如果启用了 API CORS 但没开写权限,也会卡在 405 Method Not Allowed。
实操建议:
- 本地测试优先跑
ipfs daemon --writable(注意加--writable参数,否则默认只读) - 用 curl 快速验证:
curl -X POST "http://127.0.0.1:5001/api/v0/add?stream-channels=true" -F file=@test.txt,能返回Hash才说明通路正常 - Infura 用户得去控制台开 “Write API Access”,并用带 secret 的认证头,不能只靠 project ID
HttpClient 发送 multipart/form-data 时别漏掉 file 字段名
C# 调 IPFS 的 /add 接口本质是发一个标准 multipart 表单,后端只认字段名为 file 的 part——不是 data、不是 content,就是 file。很多初学者用 MultipartFormDataContent 手动构造时,给 ByteArrayContent 指定的 name 写错,结果上传成功但返回空 Hash 或 400 错误。
实操建议:
- 构造 part 时必须写成:
new StringContent("xxx", Encoding.UTF8, "text/plain") { Headers = { { "Content-Disposition", "form-data; name=\"file\"; filename=\"a.txt\"" } } } - 更稳妥的做法是用
StreamContent包裹FileStream,name 仍为"file",避免内存爆掉大文件 - 别依赖第三方封装库(如
Ipfs.Core),它底层可能把字段名硬编码成别的,出问题难排查
上传大文件时必须设 Timeout 和分块策略
IPFS 默认对单次 /add 请求有 60 秒超时,而上传几百 MB 文件很可能超时,尤其走公网节点。更麻烦的是,HttpClient 的 Timeout 是整个请求生命周期,不是传输时间,所以即使数据在流式发送,只要总耗时超限就直接抛 TaskCanceledException。
实操建议:
- 显式设置
HttpClient.Timeout = TimeSpan.FromMinutes(10),别用默认值 - 文件 >50MB 建议先用
ipfs add --chunker=size-1048576测试分块效果,C# 里对应要传?chunker=size-1048576查询参数 - 别用
PostAsync(url, content)一气呵成,改用SendAsync(request, HttpCompletionOption.ResponseHeadersRead),拿到响应头就停,避免等完整体响应
拿到 Hash 后别直接当永久链接用
IPFS 的 Hash(比如 QmXyZ...)只是内容寻址标识,不代表文件一定长期可访问。本地节点关掉、Pinata 没 pin、Infura 没订阅持久化服务,过几天链接就 404。
实操建议:
- 上传后立刻调一次
/api/v0/pin/add?arg=QmXyZ...把它固定住(前提是你的节点支持 pin) - 生产环境别裸用
https://ipfs.io/ipfs/QmXyZ...,优先走网关代理(如 Pinata 的https://gateway.pinata.cloud/ipfs/QmXyZ...),它自带缓存和可用性兜底 - 检查返回的
Hash是否以Qm开头(v0)或bafy开头(v1),旧版客户端可能不兼容 v1,需加?cid-version=1参数强制
真正麻烦的不是怎么传上去,而是谁来保证它一直在线——IPFS 本身不承诺存储,这点容易被 SDK 文档带偏。










