Как я могу обслуживать файлы при использовании GRPC - PullRequest
1 голос
/ 26 октября 2019

Есть ли способ обработки файлов в Go с GRPC, как в варианте gin-gonic :

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

1 Ответ

0 голосов
/ 26 октября 2019

Вы не можете сделать это в точности так.
Но вы можете использовать тип proto bytes и поместить байты файла в это поле.

Также (как указано в комментариях) с большими файлами вы должны использовать потоковую передачу вместо унарного вызова. (большинство реализаций GRPC имеют ограничение 4 МБ на сообщение).

Прототип:

syntax = "proto3";

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

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

Пример реализации сервера:

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
}

Клиент будет называть это так:

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)
}

Если вам нужно иметь возможность обслуживать произвольные файлы, вам необходимо определить, какие файлы вы разрешаете обслуживать (например, кто-то запрашивает файл /etc/passwd или что-то в этом роде).
Не уверен, что именноэто именно тот случай использования здесь.

...