zoukankan      html  css  js  c++  java
  • IPFS实践之功能封装

    更多我的博客请关注:https://kekbin.com/
    IPFS实践之初体验中以命令行的方式演示了如何使用IPFS的基础命令。然而,如果基于IPFS做二次开发应用程序,就需要对这些功能进行封装。本文介绍两种调用IPFS功能的方式。

    Cmd

    以C#为例,采用启动cmd进程的方式调用命令行,命令行执行程序如下:

    public const int WAIT_FOR_EXIT = 2 * 60 * 000;
    public const int CRASH_EXIT_CODE = 0xFFFF;
    public static int RunExe(string exeFileName, string args, out string output)
    {
        output = "";
        int iCode = CRASH_EXIT_CODE;
        if (exeFileName == null)
        {
            return CRASH_EXIT_CODE;
        }
        ProcessStartInfo procInfo = new ProcessStartInfo
        {
            FileName = exeFileName,
            Arguments = args,
            RedirectStandardOutput = true,
            CreateNoWindow = true,
            UseShellExecute = false,
        };
        Process exeProc = null;
        StreamReader reader = null;
        try
        {
            exeProc = Process.Start(procInfo);
            if (exeProc == null)
            {
                return CRASH_EXIT_CODE;
            }
            if (exeProc.StandardOutput != null && exeProc.StandardOutput.BaseStream != null)
            {
                output = exeProc.StandardOutput.ReadToEnd();
            }
            if (exeProc != null)
            {
                if (exeProc.WaitForExit(WAIT_FOR_EXIT))
                {
                    iCode = exeProc.ExitCode;
                }
                int cnt = 0;
                while (!exeProc.HasExited && cnt++ < 15)
                {
                    Thread.Sleep(5);
                }
                exeProc.Close();
            }
        }
        catch (Exception e)
        {
            iCode = CRASH_EXIT_CODE;
        }
        finally
        {
            DisposeResource(reader);
        }
        return iCode;
    }    
    

    有了以上的封装,那么执行ipfs add的调用代码可以是

    string args = string.Format("add {0}", filepath)
    ScriptTool.RunExe("ipfs", args, out output);
    

    这样的方式,有几种弊端:

    • 由于每执行一条命令都需要启动一个进程,而这些进程是互斥的,在IPFS repo下,有一个repo_lock,导致同时命令多条执行的失败率很高。
    • 由于命令行执行时,执行完成的输出都是字符串,处理起来比较麻烦。

    Http Api

    之前提过,官方提供的go-ipfs实现的demo中提供了http api的功能,也就是说所有的IPFS功能都可以通过http请求的方式来实现,http接口详见http API docs
    以http api的方式执行,可以完美解决cmd的问题:

    • 它无需另起进程,可以使用多线程,同时执行多条指令
    • api的返回是一个json string,方便解析我们所需要的结果参数。

    还是以ipfs add为例,接口说明如下:

    /api/v0/add
    Add a file or directory to ipfs.
    Arguments
    arg [file]: The path to a file to be added to ipfs. Required: yes.
    recursive [bool]: Add directory paths recursively. Default: “false”. Required: no.
    quiet [bool]: Write minimal output. Required: no.
    quieter [bool]: Write only final hash. Required: no.
    silent [bool]: Write no output. Required: no.
    progress [bool]: Stream progress data. Required: no.
    trickle [bool]: Use trickle-dag format for dag generation. Required: no.
    only-hash [bool]: Only chunk and hash - do not write to disk. Required: no.
    wrap-with-directory [bool]: Wrap files with a directory object. Required: no.
    hidden [bool]: Include files that are hidden. Only takes effect on recursive add. Required: no.
    chunker [string]: Chunking algorithm to use. Required: no.
    pin [bool]: Pin this object when adding. Default: “true”. Required: no.
    raw-leaves [bool]: Use raw blocks for leaf nodes. (experimental). Required: no.
    nocopy [bool]: Add the file using filestore. (experimental). Required: no.
    fscache [bool]: Check the filestore for pre-existing blocks. (experimental). Required: no.
    cid-version [int]: Cid version. Non-zero value will change default of ‘raw-leaves’ to true. (experimental). Default: “0”. Required: no.
    hash [string]: Hash function to use. Will set Cid version to 1 if used. (experimental). Default: “sha2-256”. Required: no.
    Request Body
    Argument “path” is of file type. This endpoint expects a file in the body of the request as ‘multipart/form-data’.
    Response
    On success, the call to this endpoint will return with 200 and the following body:
    {
    "Name": "[string]"
    "Hash": "[string]"
    "Bytes": "[int64]"
    "Size": "[string]"
    }

    所以add一个文件,需要用post的方式,通过mutipart/form-data格式提交文件内容
    这个请求的url:
    http://localhost:5001/api/v0/add?recursive=false
    其中上传文件夹的时候,recursive参数需要设置为true。

    Mutipart上传代码如下

    public static string HttpMultiPartPost(string url, int timeOut, string path, bool isDir)
    {
        string responseContent;
        var webRequest = (HttpWebRequest)WebRequest.Create(url);
        // 边界符  
        var boundary = "---------------" + DateTime.Now.Ticks.ToString("x");
        // 边界符  
        var beginBoundary = Encoding.ASCII.GetBytes("
    " + "--" + boundary + "
    ");
        // 最后的结束符  
        var endBoundary = Encoding.ASCII.GetBytes("
    " + "--" + boundary + "--
    ");
        // 设置属性  
        webRequest.Method = "POST";
        webRequest.Timeout = timeOut;
        webRequest.ContentType = "multipart/form-data; boundary=" + boundary;
        var requestStream = webRequest.GetRequestStream();
        if (isDir)
        {
            //发送分割符
            requestStream.Write(beginBoundary, 0, beginBoundary.Length);
            //发送文件夹头
            const string folderHeaderFormat =
            "Content-Disposition: file; filename="{0}"
    " +
            "Content-Type: application/x-directory
    
    ";
            var folderHeader = string.Format(folderHeaderFormat, GetDirectoryName(path));
            var headerbytes = Encoding.UTF8.GetBytes(folderHeader);
            requestStream.Write(headerbytes, 0, headerbytes.Length);
            DirectoryInfo directory = new DirectoryInfo(path);
            FileInfo[] fileInfo = directory.GetFiles();
            foreach (FileInfo item in fileInfo)
            {
                PostFileItem(requestStream, beginBoundary, item, GetDirectoryName(path));
            }
        }
        else
        {
            PostFileItem(requestStream, beginBoundary, new FileInfo(path), null);
        }
        // 写入最后的结束边界符  
        requestStream.Write(endBoundary, 0, endBoundary.Length);
        requestStream.Close();
        var httpWebResponse = (HttpWebResponse)webRequest.GetResponse();
        using (var httpStreamReader = new StreamReader(httpWebResponse.GetResponseStream(),Encoding.GetEncoding("utf-8")))
        {
            responseContent = httpStreamReader.ReadToEnd();
        }
        httpWebResponse.Close();
        webRequest.Abort();
        return responseContent;
    }
    
    private static void PostFileItem(Stream stream, byte[] boundary, FileInfo fileInfo, string dirName)
    {
        string name = fileInfo.Name;
        if (!string.IsNullOrEmpty(dirName))
        {
            name = dirName + "/" + name;
        }
        //发送分割符
        stream.Write(boundary, 0, boundary.Length);
        //发送文件头
        const string headerFormat =
                        "Abspath: {0}
    " +
                        "Content-Disposition: file; filename="{1}"
    " +
                        "Content-Type: application/octet-stream
    
    ";
        var header = string.Format(headerFormat, fileInfo.FullName, name);
        var headerbytes = Encoding.UTF8.GetBytes(header);
        stream.Write(headerbytes, 0, headerbytes.Length);
        //发送文件流
        var fileStream = new FileStream(fileInfo.FullName, FileMode.Open, FileAccess.Read);
        var buffer = new byte[1024];
        int bytesRead; // =0  
        while ((bytesRead = fileStream.Read(buffer, 0, buffer.Length)) != 0)
        {
            stream.Write(buffer, 0, bytesRead);
        }
        fileStream.Close();
    }
    

    add Api调用代码如下:

    public static string Add(string path, bool isDir)
    {
        if (string.IsNullOrEmpty(path))
        {
                    return null;
        }
        string format = "";
        if (isDir)
        {
            format = "{0}?chunker=size-262144&recursive=true";
        }
        else
        {
            //如果需要保存名字,需要wrap一个dir
            //format = "{0}?chunker=size-262144&recursive=false&wrap-with-directory=true";
            format = "{0}?chunker=size-262144&recursive=false";
        }
        string url = string.Format(format, "http://localhost:5001/api/v0/add");
        string output = HttpMultiPartPost(url, 500000, path, isDir);
        return output;
    }
    

    大部分的api都是以get的方式请求,相比add简单许多,这里提供一个C#的httpget方法供参考.

    public static string HttpGet(string url, int timeOut)
    {
        string responseContent;
        var webRequest = (HttpWebRequest)WebRequest.Create(url);
        webRequest.Method = "GET";
        webRequest.Timeout = timeOut;
        var httpWebResponse = (HttpWebResponse)webRequest.GetResponse();
        using (var httpStreamReader = new StreamReader(httpWebResponse.GetResponseStream(), Encoding.GetEncoding("utf-8")))
        {
            responseContent = httpStreamReader.ReadToEnd();
        }
        httpWebResponse.Close();
        webRequest.Abort();
        return responseContent;
    }
    
    

    如name publish这样的操作就可以这样调用

    public static string NamePublish(string hash, int hours)
    {
        //lifetime失效期
        string format = "{0}?arg={1}&lifetime={2}h";
        string url = string.Format(format, @"http://localhost:5001/api/v0/name/publish", hash, hours);
        return HttpTool.HttpGet(url, 100000);
    }
    

    封装了这些操作,我们就可以将IPFS用起来,做一些我们所需要的application。

  • 相关阅读:
    Bash简单介绍
    三大框架常遇的错误:hibernate : object references an unsaved transient instance
    第二十八篇: 学习笔记
    oracle表空间查询维护命令大全之二(undo表空间)
    JSON 数据的系统解析
    MySQL字符串函数
    HDU ACM 1025 Constructing Roads In JGShining&#39;s Kingdom-&gt;二分求解LIS+O(NlogN)
    实时竞价RTB广告平台_传漾科技_中国领先的智能数字营销引擎
    秒聘网-产品经理招聘,免费、专注、极速
    “小鸟推送”从DSP做到营销效果最大化,挂牌新三板后正在寻求B轮定增
  • 原文地址:https://www.cnblogs.com/kekbin/p/9253309.html
Copyright © 2011-2022 走看看