Hi,大家好,我是姜友华。
上一节,我们实现了ProtoBuf在iOS里的应用,同时用Go搭建了一个简单的接收文件上件的服务器。这一节,我们将用Go服务接收保存日志文件并解析输出。
内容概要
- 在Go里添加ProtoBuf。
- 接收保存客户端传来的日志文件,并解析输出。
为Go添加ProtoBuf。
对Go的支持我们可以从Porto官网着手。
- 安ProtoBuf的go支持工具。
- 获取protoc-gen-go。
$ brew install protoc-gen-go
- 编辑.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指定的输出目录来设置的。
- 转换文件。
运行后在logger文件夹
$ protoc --go_out=. ./LogInfo.proto
运行后,在工程目录的logger文件夹下生成了LogInfo.pb.go文件。
解析LogInfo
我们上一节,实现了保存上传的文件,现在需要修改一下main.go
- 添一个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
- 解析为结构体。
修改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里添加上传功能。
- info.plist里添加http可用。
<key>NSAppTransportSecurity</key>
<dict>
<key>NSAllowsArbitraryLoads</key>
<true/>
</dict>
- 添加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格式导出数据表一样,我们可以导出表列名。应用于单文件保存多条日志数据时,这是一种较好的方法。
好,就到这里。我是姜友华,下一次,再见。
网友评论