IPFS 私有网络部署指南
0. 前言 & 系统版本 & 专有名词
私有网络的作用: 使IPFS不连接到IPFS公网, 并且私有网络内部每个节点的数据可以互相访问, 并且不会重复存储. (如果手动pin到本地, 则会存储)
在Ubuntu server 20.04 上完成
引导节点: 集群中,用于引导的节点,一般是一个性能较高的某一个节点
客户端节点: 集群中的其他节点
1. 每个节点安装IPFS
参考:https://docs.ipfs.tech/install/command-line/#install-official-binary-distributions
2. 每个节点 初始化
参考:https://zhuanlan.zhihu.com/p/145637930
在每个节点上执行:
IPFS_PATH=~/.ipfs ipfs init
3. 创建专用网络
- 引导节点 生成密钥:
echo "/key/swarm/psk/1.0.0/" > ~/.ipfs/swarm.key
echo "/base16/" >> ~/.ipfs/swarm.key
cat /dev/urandom | tr -dc 'a-f0-9' | head -c64 >> ~/.ipfs/swarm.key
该命令将在 .ipfs 目录下创建一个名为 swarm.key 的文件,并将以下内容写入其中:(以下内容用来对比参考, 生成的密钥对不对)
/key/swarm/psk/1.0.0/
/base16/
<64-character random hex string>
- 将生成的 swarm 文件复制到所有客户端节点的~/.ipfs 目录
使用Xftp就可以, 但是Xftp不显示隐藏文件夹, 先cp到~/ 然后 cp 到 ~/.ipfs
4. 引导IPFS节点
- 从所有节点删除节点默认条目:
IPFS_PATH=~/.ipfs ipfs bootstrap rm --all
查看结果:
IPFS_PATH=~/.ipfs ipfs config show
- 将您的引导节点的 ip 地址和对等身份(哈希地址)添加到每个节点(包括引导节点)
- 引导节点查看IP:
hostname -I
- 引导节点查看对等身份(节点哈希地址):
IPFS_PATH=~/.ipfs ipfs config show | grep "PeerID"
- 在所有节点使用如下语句添加:(注意,这是一个示例,因为每个人集群节点的IP地址和哈希地址不同)
IPFS_PATH=~/.ipfs ipfs bootstrap add /ip4/<ip address of bootnode>/tcp/4001/ipfs/<peer identity hash of bootnode>
5. 启动网络
- 所有节点设置集群不连接到公网:
export LIBP2P_FORCE_PNET=1
- 所有节点启动守护进程
export IPFS_PATH=~/.ipfs
ipfs daemon
注意: 每增加一个新的节点, 在新节点上执行的命令都要执行一次, 并且确保各节点间通信正常!
6. 结果:
-
引导节点开启端口转发:(方法任意, 可通过 ssh -L , 也可通过vscode 直接转发)
-
在宿主机浏览器输入:
localhost:5001/webui
,得到如下界面:(并且发现了两个其他节点)
- 发现的两个其他节点, 在每个节点上输入:
IPFS_PATH=~/.ipfs ipfs config show | grep "PeerID"
查看ID是否正确.
在这里插入图片描述
- 两个客户端节点的ID:
7. go-ipfs-api 调用指南
可参考:(不完善)
这个单节点的api 只能向一个节点的VFS(可变文件系统)里写数据, 主要用于IPFS节点加入公网时, 文件可方便上传到IPNS, 我们项目 使用IPFS Cluster集群作为链下存储系统支持, 这个单节点的api对我们的项目价值不大, 因此暂时先不研究这个了.
ipfs.go
package ipfs
import (
"bytes"
"context"
shell "github.com/ipfs/go-ipfs-api"
"io/ioutil"
)
type Ipfs struct {
Url string
Sh *shell.Shell
}
func NewIpfs(url string) *Ipfs {
sh := shell.NewShell(url)
return &Ipfs{
Url: url,
Sh: sh,
}
}
// UploadIPFS 上传数据到ipfs
func (i *Ipfs) UploadIPFS(str string) (hash string, err error) {
hash, err = i.Sh.Add(bytes.NewBufferString(str), shell.Pin(true))
if err != nil {
return
}
return
}
// UnPinIPFS 从ipfs上删除数据
func (i *Ipfs) UnPinIPFS(hash string) (err error) {
err = i.Sh.Unpin(hash)
if err != nil {
return
}
err = i.Sh.Request("repo/gc", hash).
Option("recursive", true).
Exec(context.Background(), nil)
if err != nil {
return
}
return nil
}
// CatIPFS 从ipfs下载数据
func (i *Ipfs) CatIPFS(hash string) (string, error) {
read, err := i.Sh.Cat(hash)
if err != nil {
return "", err
}
body, err := ioutil.ReadAll(read)
return string(body), nil
}
main.go
package main
import (
"fmt"
"ipfs_client/ipfs"
)
func main() {
ipfs_client := ipfs.NewIpfs("localhost:5001")
hash, err := ipfs_client.UploadIPFS("hello world!")
if err != nil {
fmt.Println("Failed to upload file:", err)
}
fmt.Println(hash)
hash_geted, err := ipfs_client.CatIPFS("QmT78zSuBmuS4z925WZfrqQ1qHaJ56DQaTfyMUF7F8ff5o")
if err != nil {
fmt.Println("Failed to download file:", err)
}
fmt.Println(hash_geted)
}
IPFS Cluster 集群部署指南
0. 前言(IPFS-Cluster 功能 及 IPFS-Cluster架构)
- IPFS Cluster 功能
IPFS Cluster 作为 IPFS 的一个附加组件,提供了以下功能:
- 自动化节点管理:自动化地添加、删除、替换节点。
- 数据备份和复制:数据备份和复制,以确保 IPFS 集群中的数据高可用性,并确保数据可在集群中的所有节点之间共享。
- 负载均衡:在多个 IPFS 节点之间分配数据和请求,以实现负载均衡,并确保高效的数据访问。
- 可扩展性:允许将更多的节点加入集群,以实现更大规模的数据存储和访问。
- 集群监控:提供监控 IPFS 集群和节点的实时性能指标,以便管理员可以及时检测和解决潜在的问题。
IPFS 私有网络是指在公共 IPFS 网络之外,用户可以创建自己的私有 IPFS 网络,这样他们可以在其中存储和共享数据。私有 IPFS 网络与 IPFS Cluster 最大的不同在于,私有 IPFS 网络并不提供节点管理、数据备份和负载均衡等功能,需要用户自己来管理节点、备份数据和分配请求。因此,对于需要大规模数据存储和访问的企业来说,使用 IPFS Cluster 更加便捷和可靠。
- IPFS-Cluster 节点服务架构
IPFS-Cluster节点和IPFS是一一对应的关系,先搭建上述的IPFS多节点网络,继续搭建IPFS-Cluster。官方文档
简单介绍两者的关系:
ipfs-cluster-service
启动一个cluster peer节点,它依赖于一个ipfs daemon节点;cluster节点会加入一个独立于IPFS网络的另一个swarm网络cluster peer会参与集群的共识,遵循一个关于固定pin和解除固定unpin请求的分布式日志,并且对配置的IPFS daemon管理相关的pin操作
cluster peer提供用于集群管理的API,和一个IPFS Proxy API将请求转发给IPFS daemon,以及内部通信的组件
IPFS集群管理架构:
+------------------+ | ipfs-cluster-ctl | +---------+--------+ | | HTTP(s) ipfs-cluster-service | HTTP +----------+--------+--v--+----------------------+ +-------------+ | RPC | Peer 1 | API | IPFS Connector/Proxy +------> IPFS daemon | +----^-----+--------+-----+----------------------+ +-------------+ | libp2p | +----v-----+--------+-----+----------------------+ +-------------+ | RPC | Peer 2 | API | IPFS Connector/Proxy +------> IPFS daemon | +----^-----+--------+-----+----------------------+ +-------------+ | | +----v-----+--------+-----+----------------------+ +-------------+ | RPC | Peer 3 | API | IPFS Connector/Proxy +------> IPFS daemon | +----------+--------+-----+----------------------+ +-------------+
1. 升级go版本
如果不升级go版本,在后续操作中可能会出现各种问题.
升级操作参考:https://zhuanlan.zhihu.com/p/453462046
(我升级到的go版本: go version go1.20.3 linux/amd64)
2. 下载IPFS-Cluster 源码并编译
参考:https://ipfscluster.io/download/
- 方法1: 下载 二进制文件, 设置可执行权限, 直接构建.
您可以按照以下步骤来操作:
- 打开 IPFS Cluster Download 页面 (https://ipfscluster.io/download/)。
- 根据您的操作系统选择适当的二进制文件。
- 将下载的文件解压缩到您想要运行 IPFS Cluster 的目录。例如,您可以将它解压到
/usr/local/bin/
目录。- 将二进制文件设置为可执行文件。您可以使用以下命令:
chmod +x /usr/local/bin/ipfs-cluster-service chmod +x /usr/local/bin/ipfs-cluster-ctl chmod +x /usr/local/bin/ipfs-cluster-follow
- 从源码编译运行:(不建议,会出各种问题)
git clone https://github.com/ipfs-cluster/ipfs-cluster.git
cd ipfs-cluster
make install
注意:如果安装完成后,依然出现命令未找到, 可能是你的go 环境变量有问题
可以通过以下方法将环境变量添加到 ~/.bashrc:
cd ~ vim .bashrc # 添加以下内容 export GOROOT=/usr/local/go export GOPATH=$HOME/gowork export GOBIN=$GOPATH/bin export PATH=$GOPATH:$GOBIN:$GOROOT/bin:$PATH :wq . .bashrc
3. 初始化ipfs-cluster-service
- 在第一个引导节点 初始化 ipfs-cluster-service:
ipfs-cluster-service init
- 查看 第一个节点生成的secret:
cd ~/.ipfs-cluster
vim service.json
复制生成的secret, 一个示例如下图所示:
- 在其他的客户端节点使用以下命令初始化:
CLUSTER_SECRET=<your_cluster_secret> ipfs-cluster-service init
其中, <your_cluster_secret>是你从第一个节点复制得到的secret.
4. 启动 ipfs-cluster-service 守护进程
- 在引导节点使用以下命令启动守护进程:
ipfs-cluster-service daemon
然后查看输出的 INFO , 复制该节点的ipfs-cluster 服务地址, 一个实例如下图所示:
- 在客户端节点 使用以下命令启动ipfs-cluster 守护进程, 同时从第一个引导节点引导:
ipfs-cluster-service daemon --bootstrap <your_ipfs_cluster_node0_address>
其中, <your_ipfs_cluster_node0_address> 是你从上面复制得到的.一个实例如下图所示:
- 在任意节点查看连接的peers :
ipfs-cluster-ctl peers ls
结果如下图所示: 表示集群已经发现另外两个peers节点.
IPFS-Cluster 集群 **使用 **指南
1. 查看对等节点
ipfs-cluster-ctl peers ls
2. 集群操作
官方文档:https://pkg.go.dev/github.com/ipfs-cluster/ipfs-cluster/api/rest/client#section-readme
参考示例:
package ipfs_storge
import (
"context"
"errors"
"fmt"
"github.com/ipfs-cluster/ipfs-cluster/api"
"github.com/ipfs-cluster/ipfs-cluster/api/rest/client"
"github.com/ipfs/go-cid"
shell "github.com/ipfs/go-ipfs-api"
"github.com/ipfs/go-libipfs/files"
"github.com/multiformats/go-multiaddr"
"io"
"net/http"
"os"
"strconv"
"strings"
"time"
"log"
)
type IpfsStorgeClient struct {
client client.Client
}
func NewIpfsStorgeClient() *IpfsStorgeClient {
apiaddr := "/ip4/127.0.0.1/tcp/9094"
username := "ipfs-cluster-user"
password := "ipfs-cluster-user-pass"
//参数合法性检查, 如果将上述三个参数已经封装进来了, 就不需要了
//if len(apiaddr) == 0 {
// log.Println("apiaddr is empty!")
// apiaddr = "/ip4/127.0.0.1/tcp/9094"
//}
//if len(username) == 0 || len(password) == 0 {
// username = "ipfs-cluster-user"
// password = "ipfs-cluster-user-pass"
//}
//创建一个多播地址
apiAddr, err := multiaddr.NewMultiaddr(apiaddr)
if err != nil {
log.Println("failed to create multiaddr:", err.Error())
}
//创建一个配置对象
cfg := client.Config{
Username: username,
Password: password,
APIAddr: apiAddr,
}
//建立一个客户端
ipfsClient, err := client.NewDefaultClient(&cfg)
if err != nil {
log.Println("failed to create client:", err.Error())
}
return &IpfsStorgeClient{client: ipfsClient}
}
func (i *IpfsStorgeClient) AddFile(filePath string) (api.AddedOutput, error) {
// 检查文件路径
if _, err := os.Stat(filePath); os.IsNotExist(err) {
log.Println("File does not exist: ", filePath)
return api.AddedOutput{}, err
}
err := os.Chmod(filePath, 0777)
if err != nil {
log.Println("Failed to set file permissions:", err)
}
hash, err3 := i.PinFileToIPFS(filePath)
if err3 != nil {
log.Println("failed to add file to local IPFS:", err)
}
fmt.Printf("File added to IPFS with hash: %s\n", hash)
// 打开文件
file, err := os.Open(filePath)
if err != nil {
log.Println("Error:", err)
return api.AddedOutput{}, err
}
defer func(file *os.File) {
err := file.Close()
if err != nil {
log.Println("1file close failed!")
}
}(file)
// 获取文件元数据
fileInfo, err3 := file.Stat()
if err3 != nil {
log.Println("Error:", err)
return api.AddedOutput{}, err
}
metadata := make(map[string]string)
metadata["Name"] = fileInfo.Name()
metadata["Size"] = strconv.FormatInt(fileInfo.Size(), 10)
metadata["Mode"] = fileInfo.Mode().String()
metadata["ModTime"] = fileInfo.ModTime().String()
metadata["IsDir"] = strconv.FormatBool(fileInfo.IsDir())
// 创建 上传文件的参数结构体
addParams := api.DefaultAddParams()
addParams.PinOptions = api.PinOptions{
ReplicationFactorMin: 1, //最小副本数
ReplicationFactorMax: 3, //最大副本数
Name: fileInfo.Name(),
Mode: 0,
ShardSize: 0,
Metadata: metadata,
}
//指定参数为分片上传, 如果文件特别大, 可以打开这个参数, 或者 重新封装一个
//addParams.Shard = true
//addParams.StreamChannels = true
// 创建一个输出channel
outChan := make(chan api.AddedOutput)
ctx, cancel := context.WithTimeout(context.Background(), 30*time.Second)
defer cancel()
// 创建一个线程来运行
var err2 error
go func() {
err2 = i.client.Add(ctx, []string{filePath}, addParams, outChan)
}()
// 如果通道有数据,则返回
select {
case addedOutput := <-outChan:
//Pinning 文件
ctx, cancel := context.WithTimeout(context.Background(), 30*time.Second)
defer cancel()
_, err := i.client.Pin(ctx, addedOutput.Cid, addParams.PinOptions)
if err != nil {
log.Println("Failed to pin file:", err)
// 处理 pin 失败的情况
}
if err2 != nil {
return api.AddedOutput{}, fmt.Errorf("failed to add file to IPFS Cluster: %v", err2)
}
fmt.Printf("File added to IPFS Cluster with CID: %s\n", addedOutput.Cid)
return addedOutput, err2
case <-ctx.Done():
log.Println("timeout while waiting for IPFS Cluster output")
// 进行其他处理,如结束程序或返回错误信息
//2.
return api.AddedOutput{}, errors.New("timeout while waiting for IPFS Cluster output")
}
}
func (i *IpfsStorgeClient) PinFileToIPFS(filePath string) (string, error) {
// 打开文件
file, err := os.Open(filePath)
if err != nil {
log.Println("Error:", err)
return "", err
}
//使用ipfs client 将文件pin到本地
ctx, cancel := context.WithTimeout(context.Background(), 30*time.Second)
defer cancel()
shellInstance := i.client.IPFS(ctx)
hash, err3 := shellInstance.Add(file, shell.Pin(true))
//err = shellInstance.Pin(filePath)
//if err != nil {
// log.Println("failed to get file!", err)
// return addedOutput, err
//}
err = file.Close()
if err != nil {
log.Println("2file close failed!")
}
return hash, err3
}
// Allocation returns the current allocations for a given Cid.
func (i *IpfsStorgeClient) Allocation(cidStr string) (api.Pin, error) {
ctx, cancel := context.WithTimeout(context.Background(), 30*time.Second)
defer cancel()
fileCid, err := cid.Parse(cidStr)
if err != nil {
log.Println("cid decode failed!", err)
}
allocation, err2 := i.client.Allocation(ctx, api.Cid{Cid: fileCid})
//if err2 != nil {
// log.Println("failed to allocation with cid!",err2)
//}
return allocation, err2
}
// Status returns the current ipfs state for a given Cid.
func (i *IpfsStorgeClient) Status(cidStr string) (api.GlobalPinInfo, error) {
ctx, cancel := context.WithTimeout(context.Background(), 30*time.Second)
defer cancel()
fileCid, err := cid.Parse(cidStr)
if err != nil {
log.Println("cid decode failed!", err)
}
allocation, err2 := i.client.Status(ctx, api.Cid{Cid: fileCid}, false)
//if err2 != nil {
// log.Println("failed to allocation with cid!",err2)
//}
return allocation, err2
}
func (i *IpfsStorgeClient) DeleteFile(cidStr string) (map[string]api.RepoGC, error) {
ctx, cancel := context.WithTimeout(context.Background(), 30*time.Second)
defer cancel()
fileCid, err := cid.Parse(cidStr)
if err != nil {
log.Println("cid decode failed!", err)
}
_, err2 := i.client.Unpin(ctx, api.Cid{Cid: fileCid})
if err2 != nil {
log.Println("failed to allocation with cid!", err2)
}
repo, err3 := i.client.RepoGC(ctx, false)
if err3 != nil {
log.Println("garbage collection on IPFS daemons of cluster peers failed!", err3)
}
//fmt.Println("GlobalRepoGC peer map len:", len(repo.PeerMap))
return repo.PeerMap, err2
}
func (i *IpfsStorgeClient) GetFile2(cidStr, path string) error {
ctx, cancel := context.WithTimeout(context.Background(), 30*time.Second)
defer cancel()
_, err := os.Create(path)
if err != nil {
log.Println("failed to create File :", err)
}
err = os.Chmod(path, 0777)
if err != nil {
log.Println("Failed to set file permissions:", err)
}
shellInstance := i.client.IPFS(ctx)
err = shellInstance.Get(cidStr, path)
if err != nil {
log.Println("failed to get file!", err)
return err
}
return nil
}
func (i *IpfsStorgeClient) GetConnectGraph() {
ctx, cancel := context.WithTimeout(context.Background(), 30*time.Second)
defer cancel()
graph, err := i.client.GetConnectGraph(ctx)
if err != nil {
panic(err)
}
// print the graph
fmt.Printf("%+v\n", graph)
}
func (i *IpfsStorgeClient) GetFileDag(cidStr string) {
ctx, cancel := context.WithTimeout(context.Background(), 30*time.Second)
defer cancel()
shellInstance := i.client.IPFS(ctx)
//shell2 :=shell.NewShell("localhost:5001")
c, err := cid.Decode(cidStr)
if err != nil {
log.Println("failed to decode cid", err)
}
fmt.Println("CID:", c)
var out map[string]interface{}
err = shellInstance.DagGet(cidStr, &out)
if err != nil {
log.Println("err to get dag", err)
}
printDagTree(shellInstance, cidStr, 0)
}
func printDagTree(shell *shell.Shell, cidStr string, indent int) {
// 获取当前节点的内容
var out map[string]interface{}
err := shell.DagGet(cidStr, &out)
if err != nil {
log.Println("failed to get dag", err)
return
}
// 打印当前节点的 CID 和内容
fmt.Printf("%s%s\n", strings.Repeat(" ", indent), cidStr)
for k, v := range out {
fmt.Printf("%s%s: %v\n", strings.Repeat(" ", indent+2), k, v)
}
// 遍历当前节点的所有子节点,并递归打印出它们的树状结构
links, ok := out["Links"].([]interface{})
if ok {
for _, link := range links {
linkMap, ok := link.(map[string]interface{})
if ok {
childCid, ok := linkMap["Hash"].(string)
if ok {
printDagTree(shell, childCid, indent+2)
}
}
}
}
}
func (i *IpfsStorgeClient) AddDirectory(directoryPath string) (api.AddedOutput, error) {
// 读取要添加到IPFS Cluster的目录
//includeHidden := true
// 获取目录信息, 校验目录是否存在
fileInfo, err0 := os.Stat(directoryPath)
if err0 != nil {
log.Println("failed to getting file info:", err0)
}
// 建立元数据信息
metadata := make(map[string]string)
metadata["Name"] = fileInfo.Name()
metadata["Size"] = strconv.FormatInt(fileInfo.Size(), 10)
metadata["Mode"] = fileInfo.Mode().String()
metadata["ModTime"] = fileInfo.ModTime().String()
metadata["IsDir"] = strconv.FormatBool(fileInfo.IsDir())
// 构造目录 Node
directoryNode, err := files.NewSerialFile(directoryPath, true, fileInfo)
if err != nil {
log.Printf("Error creating directory node: %v\n", err)
}
// 将接口类型转换为 Directory
directory, ok := directoryNode.(files.Directory)
if !ok {
// 处理类型转换失败的错误
log.Println("failed conver Node to Directory!")
}
// 构造 MultiFileReader
multiFileReader := files.NewMultiFileReader(directory, true)
// 设置AddParams
addParams := api.DefaultAddParams()
addParams.PinOptions = api.PinOptions{
ReplicationFactorMin: 1,
ReplicationFactorMax: 3,
Metadata: metadata,
}
// 创建AddedOutput通道
outChan := make(chan api.AddedOutput)
ctx, cancel := context.WithTimeout(context.Background(), 30*time.Second)
defer cancel()
// 调用AddMultiFile方法添加目录到IPFS Cluster
var err2 error
go func() {
err2 = i.client.AddMultiFile(ctx, multiFileReader, addParams, outChan)
}()
// 从AddedOutput通道读取结果并打印
select {
case addedOutput := <-outChan:
fmt.Printf("Directory added to IPFS Cluster with CID: %s\n", addedOutput.Cid)
return addedOutput, err2
case <-ctx.Done():
log.Println("timeout while waiting for IPFS Cluster output")
return api.AddedOutput{}, errors.New("timeout while waiting for IPFS Cluster output")
}
}
func (i *IpfsStorgeClient) Test() {
ctx, cancel := context.WithTimeout(context.Background(), 30*time.Second)
defer cancel()
fmt.Println("----------------------------------------------------------------")
out := make(chan api.Pin)
var err3 error
go func() {
err3 := i.client.Allocations(ctx, api.DataType, out)
if err3 != nil {
log.Println("failed to retrieve allocations!", err3)
}
}()
if err3 != nil {
log.Println("failed to retrieve allocations!", err3)
}
for p := range out {
log.Printf("Received allocation for CID %s with status %s\n", p.Cid, p.Allocations)
}
}
func (i *IpfsStorgeClient) GetFile(cidStr, path string) error {
resp, err := http.Get("http://localhost:5001/api/v0/cat/" + cidStr)
if err != nil {
log.Fatal(err)
}
defer func(Body io.ReadCloser) {
err := Body.Close()
if err != nil {
}
}(resp.Body)
if resp.StatusCode != http.StatusOK {
log.Fatal("Failed to retrieve file:", resp.Status)
}
file, err := os.Create(path)
if err != nil {
log.Fatal(err)
}
defer func(file *os.File) {
err := file.Close()
if err != nil {
}
}(file)
_, err = io.Copy(file, resp.Body)
if err != nil {
log.Fatal(err)
}
fmt.Println("File downloaded successfully.")
return nil
}
- main.go
package main
import (
"co-storage-server/ipfs_storge"
"log"
)
func main111() {
ipfsStorgeClient := ipfs_storge.NewIpfsStorgeClient()
//filePath := "/var/www/uploads/yjy/ZUSC-D-22-00496_H.pdf"
//addedOutput, err := ipfsStorgeClient.AddFile(filePath)
//if err != nil {
// log.Println("failed to add file: ", err)
//}
//fmt.Println(addedOutput)
//fmt.Println("------------------------------------------------------------")
//directoryPath := "/home/yjy/go/src/co-storge-server/ipfs_storge"
//dirAddedOutput, err2 := ipfsStorgeClient.AddDirectory(directoryPath)
//if err2 != nil {
// log.Println("failed to add directory: ", err2)
//}
//fmt.Printf("%s, \n", dirAddedOutput)
//
//fmt.Println("----------------------------------------------------------")
ipfsStorgeClient.Test()
//cidStr := "QmVzEWRRjWpc1zAj4WJ7DpJvf4ymAse4RiAoKgG77E4oKi"
//allocation, err3 := ipfsStorgeClient.Allocation(cidStr)
//if err3 != nil {
// log.Println("failed to allocation with cid!", err3)
//
//}
//
//fmt.Println("Cid:", allocation.Cid)
//fmt.Println("PinTime:", allocation.Timestamp.String())
//fmt.Println("ReplicationFactorMax:", allocation.ReplicationFactorMax)
//fmt.Println("PeerMap:", allocation.Allocations)
//fmt.Println("Metadata:", allocation.Metadata)
//
//fmt.Println("-----------------------------------------------------------")
//
//info, err4 := ipfsStorgeClient.Status("QmVzEWRRjWpc1zAj4WJ7DpJvf4ymAse4RiAoKgG77E4oKi")
//if err4 != nil {
// log.Println("failed to get pin status:", err4)
//}
//fmt.Printf("CID: %s\nName: %s\nAllocations: %v\nOrigins: %v\nCreated: %s\nMetadata: %v\nPeerMap: %v\n",
// info.Cid, info.Name, info.Allocations, info.Origins, info.Created, info.Metadata, info.PeerMap)
//
//fmt.Println("||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||")
//ipfsStorgeClient.GetConnectGraph()
//allocation2, err5 := ipfsStorgeClient.DeleteFile("QmfWaWXSkqyjQ1zfevRiFMMLTEfeUdaGoimDBgRqBPhGzY")
//if err5 != nil {
// log.Println("failed to delete file! ", err5)
//}
//
//fmt.Println("Allocation PeerMap:", allocation2)
filepathForSave := "/var/www/uploads/test/Test_simulation.pdf"
cidStr2 := "QmfMyHZhsWDNiKNUsopTWufDxdKNUYW9yQ8vWtCi6UMocH"
err6 := ipfsStorgeClient.GetFile2(cidStr2, filepathForSave)
if err6 != nil {
log.Println("failed to get file! ", err6)
}
//fmt.Println("||||||||||||||||||||||||||||||||||||||||||||||||||||")
//roothash, err := ipfsStorgeClient.GetObjectRootHash(cidStr2)
//if err != nil {
// log.Println("get root hash failed", err)
//}
//fmt.Println(roothash)
//hash, err7 := file_verify.HashFile(filePath)
//if err7 != nil {
// log.Println("failed to get file hash!", err7)
//}
//fmt.Println("hash:", hash)
//filePath2 := "/home/yjy/go/src/co-storge-server/ipfs_storge/数字媒体三等.jpg"
//err8 := reed_solomon.EncodeFileRSToOneParity(filePath2)
//if err8 != nil {
// log.Println("err in encode file use RS to one parity!", err8)
//}
//err9 := reed_solomon.RecoverFileFromRS(filePath2)
//if err9 != nil {
// log.Println("failed to revocer file using RS!", err9)
//}
}