How can I serve files while using GRPC

China☆狼群 提交于 2020-12-06 08:25:40

问题


Is there any way how to serve files in Go with GRPC, like in gin-gonic's variant:

router.Static("/static", "/var/www")

回答1:


You can't do it exactly like that.
But you can use the proto bytes type and put the file bytes in that field.

Also (as pointed out in the comments) with large files you should use streaming instead of a unary call. (most GRPC implementation have a limit of 4MB per message).

Proto example:

syntax = "proto3";

message Response {
    bytes fileChunk = 1;
}
message Request {
    string fileName = 1;
}

service TestService {
    rpc Download(Request) returns (stream Response);
}

Server implementation example:

func (srv *Server) Download(req *pbgo.Request, responseStream pbgo.TestService_DownloadServer) error {
    bufferSize := 64 *1024 //64KiB, tweak this as desired
    file, err := os.Open(req.GetFileName())
    if err != nil {
        fmt.Println(err)
        return err
    }
    defer file.Close()
    buff := make([]byte, bufferSize)
    for {
        bytesRead, err := file.Read(buff)
        if err != nil {
            if err != io.EOF {
                fmt.Println(err)
            }
            break
        }
        resp := &pbgo.Response{
            FileChunk: buff[:bytesRead],
        }
        err = responseStream.Send(resp)
        if err != nil {
            log.Println("error while sending chunk:", err)
            return err
        }
    }
    return nil
}

Client would call it like this:

conn, err := grpc.Dial("localhost:9090", grpc.WithInsecure())
if err != nil {
    log.Fatal("client could connect to grpc service:", err)
}
c := pbgo.NewTestServiceClient(conn)
fileStreamResponse, err := c.Download(context.TODO(), &pbgo.Request{
    FileName: "test.txt",
})
if err != nil {
    log.Println("error downloading:", err)
    return
}
for {
    chunkResponse, err := fileStreamResponse.Recv()
    if err == io.EOF {
        log.Println("received all chunks")
        break
    }
    if err != nil {
        log.Println("err receiving chunk:", err)
        break
    }
    log.Printf("got new chunk with data: %s \n", chunkResponse.FileChunk)
}

If you need to be able to serve arbitrary files, you would need to handle which files you allow serving (say someone requests the file /etc/passwd or something).
Not sure what exactly is the use case here.



来源:https://stackoverflow.com/questions/58566016/how-can-i-serve-files-while-using-grpc

标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!