IPFS Cluster 集群部署、使用指南

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. 创建专用网络

  1. 引导节点 生成密钥:
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>

在这里插入图片描述

  1. 将生成的 swarm 文件复制到所有客户端节点的~/.ipfs 目录

​ 使用Xftp就可以, 但是Xftp不显示隐藏文件夹, 先cp到~/ 然后 cp 到 ~/.ipfs

4. 引导IPFS节点

  1. 所有节点删除节点默认条目:
IPFS_PATH=~/.ipfs ipfs bootstrap rm --all

查看结果:

IPFS_PATH=~/.ipfs ipfs config show
  1. 将您的引导节点的 ip 地址和对等身份(哈希地址)添加到每个节点(包括引导节点)
  1. 引导节点查看IP:
hostname -I
  1. 引导节点查看对等身份(节点哈希地址):
IPFS_PATH=~/.ipfs ipfs config show | grep "PeerID"
  1. 所有节点使用如下语句添加:(注意,这是一个示例,因为每个人集群节点的IP地址和哈希地址不同)
IPFS_PATH=~/.ipfs ipfs bootstrap add /ip4/<ip address of bootnode>/tcp/4001/ipfs/<peer identity hash of bootnode>

在这里插入图片描述

5. 启动网络

  1. 所有节点设置集群不连接到公网:
export LIBP2P_FORCE_PNET=1
  1. 所有节点启动守护进程
export IPFS_PATH=~/.ipfs
ipfs daemon 

注意: 每增加一个新的节点, 在新节点上执行的命令都要执行一次, 并且确保各节点间通信正常!

6. 结果:

  1. 引导节点开启端口转发:(方法任意, 可通过 ssh -L , 也可通过vscode 直接转发)
    在这里插入图片描述

  2. 在宿主机浏览器输入:

​ localhost:5001/webui

,得到如下界面:(并且发现了两个其他节点)
在这里插入图片描述
在这里插入图片描述

  1. 发现的两个其他节点, 在每个节点上输入:
IPFS_PATH=~/.ipfs ipfs config show | grep "PeerID"

查看ID是否正确.
在这里插入图片描述

  1. 两个客户端节点的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架构)

  1. IPFS Cluster 功能

IPFS Cluster 作为 IPFS 的一个附加组件,提供了以下功能:

  1. 自动化节点管理:自动化地添加、删除、替换节点。
  2. 数据备份和复制:数据备份和复制,以确保 IPFS 集群中的数据高可用性,并确保数据可在集群中的所有节点之间共享。
  3. 负载均衡:在多个 IPFS 节点之间分配数据和请求,以实现负载均衡,并确保高效的数据访问。
  4. 可扩展性:允许将更多的节点加入集群,以实现更大规模的数据存储和访问。
  5. 集群监控:提供监控 IPFS 集群和节点的实时性能指标,以便管理员可以及时检测和解决潜在的问题。

IPFS 私有网络是指在公共 IPFS 网络之外,用户可以创建自己的私有 IPFS 网络,这样他们可以在其中存储和共享数据。私有 IPFS 网络与 IPFS Cluster 最大的不同在于,私有 IPFS 网络并不提供节点管理、数据备份和负载均衡等功能,需要用户自己来管理节点、备份数据和分配请求。因此,对于需要大规模数据存储和访问的企业来说,使用 IPFS Cluster 更加便捷和可靠。

在这里插入图片描述

  1. 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. 方法1: 下载 二进制文件, 设置可执行权限, 直接构建.

您可以按照以下步骤来操作:

  1. 打开 IPFS Cluster Download 页面 (https://ipfscluster.io/download/)。
  2. 根据您的操作系统选择适当的二进制文件。
  3. 将下载的文件解压缩到您想要运行 IPFS Cluster 的目录。例如,您可以将它解压到 /usr/local/bin/ 目录。
  4. 将二进制文件设置为可执行文件。您可以使用以下命令:
chmod +x /usr/local/bin/ipfs-cluster-service
chmod +x /usr/local/bin/ipfs-cluster-ctl
chmod +x /usr/local/bin/ipfs-cluster-follow
  1. 从源码编译运行:(不建议,会出各种问题)
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

  1. 在第一个引导节点 初始化 ipfs-cluster-service:
ipfs-cluster-service init
  1. 查看 第一个节点生成的secret:
cd ~/.ipfs-cluster
vim service.json

复制生成的secret, 一个示例如下图所示:

在这里插入图片描述

  1. 在其他的客户端节点使用以下命令初始化:
CLUSTER_SECRET=<your_cluster_secret> ipfs-cluster-service init

其中, <your_cluster_secret>是你从第一个节点复制得到的secret.

4. 启动 ipfs-cluster-service 守护进程

  1. 在引导节点使用以下命令启动守护进程:
ipfs-cluster-service daemon

然后查看输出的 INFO , 复制该节点的ipfs-cluster 服务地址, 一个实例如下图所示:
在这里插入图片描述

  1. 在客户端节点 使用以下命令启动ipfs-cluster 守护进程, 同时从第一个引导节点引导:
ipfs-cluster-service daemon --bootstrap <your_ipfs_cluster_node0_address>

其中, <your_ipfs_cluster_node0_address> 是你从上面复制得到的.一个实例如下图所示:

在这里插入图片描述

  1. 在任意节点查看连接的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
}
  1. 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)
	//}
}
  • 1
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值