美文网首页
ProtoBuf在Go中的使用

ProtoBuf在Go中的使用

作者: Jiangyouhua | 来源:发表于2021-08-22 19:01 被阅读0次

Hi,大家好,我是姜友华。
上一节,我们实现了ProtoBuf在iOS里的应用,同时用Go搭建了一个简单的接收文件上件的服务器。这一节,我们将用Go服务接收保存日志文件并解析输出。

内容概要

  1. 在Go里添加ProtoBuf。
  2. 接收保存客户端传来的日志文件,并解析输出。

为Go添加ProtoBuf。

对Go的支持我们可以从Porto官网着手。

  1. 安ProtoBuf的go支持工具。
  • 获取protoc-gen-go。
$ brew install protoc-gen-go
  1. 编辑.proto文件。
    把上一节LogInfo.proto复制过来。还需要添加一些内容。
syntax = "proto3";
package logger;

option go_package = "./logger";

message LogInfo {
    int32 id = 1;
    string content = 2;
    enum Event {
        UNIVERSAL = 0;
        WEB = 1;
        IMAGES = 2;
        LOCAL = 3;
        NEWS = 4;
        PRODUCTS = 5;
        VIDEO = 6;
    }
    enum State {
        SUCCESS = 0;
        INFO = 1;
        ALART = 2;
        ERROR = 3;
    }
    Event event = 3;
    State state = 4;
}

跟Swift中的定义有2点不同:

  • 首先,package logger指定了包名。
  • 其次,option go_package = "./logger";指定了生成文件的保存目录。这个目录跟据下面protoc指定的输出目录来设置的。
  1. 转换文件。
    运行后在logger文件夹
$ protoc --go_out=. ./LogInfo.proto

运行后,在工程目录的logger文件夹下生成了LogInfo.pb.go文件。

解析LogInfo

我们上一节,实现了保存上传的文件,现在需要修改一下main.go

  1. 添一个readLinesFromFile方法,将上传的文件内容逐行读出,并打印出来。
/// main.go
func uploadFile(w http.ResponseWriter, r *http.Request) {
        ......
        defer file.Close()
    readLinesFromFile(file)
        ......
}

func readLinesFromFile(file multipart.File) {
    scanner := bufio.NewScanner(file)
    for scanner.Scan() {
        fmt.Println(scanner.Text())
    }
    if err := scanner.Err(); err != nil {
        log.Fatal(err)
    }
}

启动服务器

$ go mod tidy
$ go rum main.go

结果显示如下:

期一Monday简写为Mon 
星期二Tuesday简写为Tue 
星期三Wednesday简写为Wed 
星期四Thursday简写为Thu 
期五Friday简写为Fri 
星期六Saturday简写为Sat 
期日Sunday简写为Sun 
  1. 解析为结构体。
    修改readLinesFromFile,将读每一个的文本改为读每一行的Bytes数据。然后传到protoUnmarshal的方法里输出。
/// main.go
func readLinesFromFile(file multipart.File) {
    scanner := bufio.NewScanner(file)
    for scanner.Scan() {
        b := scanner.Bytes()
        protoUnmarshal(b)
    }
    if err := scanner.Err(); err != nil {
        log.Fatal(err)
    }
}

// 输出为LogInfo
func protoUnmarshal(b []byte) {
    logInfo := logger.LogInfo{}
    if err := proto.Unmarshal(b, &logInfo); err != nil {
        log.Fatal(err)
    }
    println(logInfo.Id, logInfo.Content, logInfo.Event, logInfo.State)
}

使用Postman测试一下,输入结果如下:

0 星期一Monday简写为Mon 1 1
1 星期二Tuesday简写为Tue 1 1
2 星期三Wednesday简写为Wed 1 1
3 星期四Thursday简写为Thu 1 1
4 星期五Friday简写为Fri 1 1
5 星期六Saturday简写为Sat 1 1
6 星期日Sunday简写为Sun 1 1

确认为:输出的结果与输入的结构相匹配。

在Swift里添加上传功能。

  1. info.plist里添加http可用。
    <key>NSAppTransportSecurity</key>
    <dict>
        <key>NSAllowsArbitraryLoads</key>
        <true/>
    </dict>
  1. 添加Swift上传代码。
/// Logger.swift
func upload(){
        guard let url = URL(string: "http://127.0.0.1"), let file = todayUrl else {
            print("URL is error")
            return
        }
        guard let data = try? Data(contentsOf: file) else {
            print("Data is error")
            return
        }
        
        var request = URLRequest(url: url, cachePolicy: .reloadIgnoringCacheData)
        request.httpMethod = "POST"
        request.timeoutInterval = 30;

        // 处理数据
        let boundary = UUID().uuidString
        request.addValue("multipart/form-data;boundary=\(boundary)", forHTTPHeaderField: "Content-Type")

        var body = Data();
        body.append("--\(boundary)\r\n".data(using: .utf8)!)
        body.append("Content-Disposition:form-data;name=\"file\";filename=\"\(file.lastPathComponent)\"\r\n".data(using: .utf8)!)
        body.append("Content-Type:text/plain; charset=utf-8\r\n\r\n".data(using: .utf8)!)
        body.append(data)
        body.append("\r\n".data(using: .utf8)!)
        body.append("--\(boundary)--\r\n".data(using: .utf8)!)
        
        let session = URLSession.init(configuration: URLSessionConfiguration.default)
        let task = session.uploadTask(with: request, from: body) { (data, response, error) in
            print(data ?? "data is nil", response ?? "response is nil", error ?? "error is nil")
        }
        task.resume()
        
    }

由于Swifti没有类似于Javascript的FormData数据,所以在按传统方式上传文件时有尴尬,需要手动拼接数据。调用该方法即可上传数据。

到现在,我们完成了在客户端使用ProtoBuf,上传到服务端,服务端保存并解析的整过流程。从应用中可以看出,ProtoBuf的使用实际上还是基于一种约定,与CSV格式相差不大,缺点也一样明显:修饰以适应数据结构变化的成本比较大。

对于以类CSV格式保存的日志,常用一种折衷的方法,将数据结构保存在文件头,就像我们用CSV格式导出数据表一样,我们可以导出表列名。应用于单文件保存多条日志数据时,这是一种较好的方法。

好,就到这里。我是姜友华,下一次,再见。

相关文章

网友评论

      本文标题:ProtoBuf在Go中的使用

      本文链接:https://www.haomeiwen.com/subject/iukibltx.html