1.构建protoc文件
syntax="proto3";
package stbserver;
message FileMessage{
string filename=1;
string filetype=2;
bytes filedata=3;
bool iscarry=4;
}
message FileResult{
int64 filenumber=1;
bool iscarry=2;
}
service StbServer{
//rpc ServerTest()returns(){}不能使用参数或者返回值为空的服务
rpc SendFile(stream FileMessage)returns(FileResult){}
}
2.书写服务文件,功能是连接后新建一个文件,然后把本次连接内的数据流写入该文件
package stboutserver
import (
"context"
"log"
"os"
"path/filepath"
"stbweb/lib/external_service/stbserver"
"sync"
)
const (
//Port 服务端口
Port = ":5000"
)
//StbServe 外部调用结构体
type StbServe struct{}
//SendFile 文件传输
func (s *StbServe) SendFile(cli stbserver.StbServer_SendFileServer) error {
fDir, err := os.Executable()
if err != nil {
panic(err)
}
fURL := filepath.Join(filepath.Dir(fDir), "assets")
mkdir(fURL)
//以上是检查文件路径,以及下面是生产备用文件,其他文件请自行定义
f, err := os.Create(filepath.Join(fURL, "test.json"))
if err != nil {
return err
}
defer f.Close()
for {
da, err := cli.Recv()
if err != nil {
log.Println("err:", err)
break
}
log.Println("name:", da.Filename)
f.Write(da.Filedata)//主要是这里,把每次接收到的字节写入
}
return nil
}
func mkdir(url string) {
_, err := os.Stat(url)
if err == nil {
return
}
if os.IsNotExist(err) {
log.Println("创建目录")
os.MkdirAll(url, os.ModePerm)
}
}
func main(){
lis, err := net.Listen("tcp", Port)
if err != nil {
panic(err)
}
s := grpc.NewServer()
stbserver.RegisterStbServerServer(s, &StbServe{})
s.Serve(lis)
}
3.客户端文件,只要把文件以字节流的形式传输,
第一种,传一个小文件
import (
"context"
"io"
"log"
"os"
"stbweb/lib/external_service/stbserver"
"strconv"
"time"
"github.com/pborman/uuid"
"google.golang.org/grpc"
_ "google.golang.org/grpc/balancer/grpclb"
)
const port = "localhost:5000"
func main() {
conn, err := grpc.Dial(port, grpc.WithInsecure())
if err != nil {
panic(err)
}
defer conn.Close()
c := stbserver.NewStbServerClient(conn) //新建client
sendfile(c)
}
func sendfile(c stbserver.StbServerClient) {
res, err := c.SendFile(context.Background())
if err != nil {
log.Println(err)
return
}
f, err := os.Open("./test.json")
if err != nil {
panic(err)
}
sta, err := f.Stat()
if err != nil {
panic(err)
}
log.Println("size:", sta.Size())//注意传一个刚好的buf进去
defer f.Close()
buf := make([]byte, sta.Size())
i := 1
for {
_, err := f.Read(buf)
if err != nil && err != io.EOF {
break
}
if err == io.EOF {
log.Println(err)
break
}
res.Send(&stbserver.FileMessage{
Filename: strconv.Itoa(i),
Filetype: "json",
Filedata: buf,//传入字节,这个类型自己定义,可以根据自己业务改变
Iscarry: true,
})
i++
}
time.Sleep(time.Second * 2)//注意传输完成前不能关闭连接,不同业务内注意
}
2.大文件传输,其他的都一样,唯一不同的是,小文件直接将所有读取到内存中,但是大文件不可能一下都读取进来,这里就需要指定每次传送的字节流大小。需要注意的是,在指定好buf空间大小的时候,最后一次如果不把这个空间填满(比如文件大小为1000,每次读400,那第三次就是200空200),这种情况下接收方会还是接收到完整大小的buf空间,会对内容有影响,所以这里会进行一定量的计算,主要是最后一次传输的大小得和剩余大小相同。
func sendBigFile(c stbserver.StbServerClient) {
f, err := os.Open("./test.json")
if err != nil {
panic(err)
}
defer f.Close()
fInfo, err := f.Stat()
if err != nil {
panic(err)
}
// log.Println(fInfo.Size())
fSize := fInfo.Size()
i := 1
res, err := c.SendFile(context.Background())
if err != nil {
panic(err)
}
for {
bufSize := 200//定义每次传输大小
if int64(200*i) > fSize && int64(200*(i-1)) < fSize {//判断如果是最后一次,大小重新计算
bufSize = int(fSize) - ((i - 1) * 200)
}
buf := make([]byte, bufSize)
_, err := f.Read(buf)
if err != nil && err != io.EOF {
break
}
if err == io.EOF {
log.Println(err)
break
}
res.Send(&stbserver.FileMessage{
Filename: strconv.Itoa(i),
Filetype: "json",
Filedata: buf,
Iscarry: true,
})
i++
}
time.Sleep(time.Second * 2)
return
}
网友评论