github.com/tickoalcantara12/micro/v3@v3.0.0-20221007104245-9d75b9bcbab9/service/store/client/blob.go (about) 1 package client 2 3 import ( 4 "bytes" 5 "context" 6 "io" 7 "net/http" 8 9 pb "github.com/tickoalcantara12/micro/v3/proto/store" 10 "github.com/tickoalcantara12/micro/v3/service/client" 11 "github.com/tickoalcantara12/micro/v3/service/errors" 12 "github.com/tickoalcantara12/micro/v3/service/store" 13 ) 14 15 const bufferSize = 1024 16 17 // NewBlobStore returns a new store service implementation 18 func NewBlobStore() store.BlobStore { 19 return &blob{} 20 } 21 22 type blob struct { 23 client pb.BlobStoreService 24 } 25 26 func (b *blob) Read(key string, opts ...store.BlobOption) (io.Reader, error) { 27 // validate the key 28 if len(key) == 0 { 29 return nil, store.ErrMissingKey 30 } 31 32 // parse the options 33 var options store.BlobOptions 34 for _, o := range opts { 35 o(&options) 36 } 37 38 // execute the rpc 39 stream, err := b.cli().Read(context.TODO(), &pb.BlobReadRequest{ 40 Key: key, 41 Options: &pb.BlobOptions{ 42 Namespace: options.Namespace, 43 }, 44 }, client.WithAuthToken()) 45 46 // handle the error 47 if verr := errors.FromError(err); verr != nil && verr.Code == http.StatusNotFound { 48 return nil, store.ErrNotFound 49 } else if verr != nil { 50 return nil, verr 51 } else if err != nil { 52 return nil, err 53 } 54 55 // create a buffer to store the bytes in 56 buf := bytes.NewBuffer(nil) 57 58 // keep recieving bytes from the stream until it's closed by the server 59 for { 60 res, err := stream.Recv() 61 if err == io.EOF { 62 break 63 } else if err != nil { 64 return nil, err 65 } 66 67 buf.Write(res.Blob) 68 } 69 70 // return the bytes 71 return buf, nil 72 } 73 74 func (b *blob) Write(key string, blob io.Reader, opts ...store.BlobOption) error { 75 // validate the key 76 if len(key) == 0 { 77 return store.ErrMissingKey 78 } 79 80 // parse the options 81 var options store.BlobOptions 82 for _, o := range opts { 83 o(&options) 84 } 85 86 // setup a context 87 ctx, cancel := context.WithCancel(context.TODO()) 88 defer cancel() 89 90 // open the stream 91 stream, err := b.cli().Write(ctx, client.WithAuthToken()) 92 if verr := errors.FromError(err); verr != nil { 93 return verr 94 } else if err != nil { 95 return err 96 } 97 98 // read from the blob and stream it to the server 99 buffer := make([]byte, bufferSize) 100 for { 101 num, err := blob.Read(buffer) 102 if err == io.EOF { 103 break 104 } else if err != nil { 105 return err 106 } 107 108 req := &pb.BlobWriteRequest{ 109 Key: key, 110 Options: &pb.BlobOptions{ 111 Namespace: options.Namespace, 112 Public: options.Public, 113 ContentType: options.ContentType, 114 }, 115 Blob: buffer[:num], 116 } 117 118 if err := stream.Send(req); err != nil { 119 return err 120 } 121 } 122 123 // wait for the server to process the blob 124 _, err = stream.CloseAndRecv() 125 return err 126 } 127 128 func (b *blob) Delete(key string, opts ...store.BlobOption) error { 129 // validate the key 130 if len(key) == 0 { 131 return store.ErrMissingKey 132 } 133 134 // parse the options 135 var options store.BlobOptions 136 for _, o := range opts { 137 o(&options) 138 } 139 140 // execute the rpc 141 _, err := b.cli().Delete(context.TODO(), &pb.BlobDeleteRequest{ 142 Key: key, 143 Options: &pb.BlobOptions{ 144 Namespace: options.Namespace, 145 }, 146 }, client.WithAuthToken()) 147 148 // handle the error 149 if verr := errors.FromError(err); verr != nil && verr.Code == http.StatusNotFound { 150 return store.ErrNotFound 151 } else if verr != nil { 152 return verr 153 } 154 155 return err 156 } 157 158 func (b *blob) cli() pb.BlobStoreService { 159 if b.client == nil { 160 b.client = pb.NewBlobStoreService("store", client.DefaultClient) 161 } 162 return b.client 163 } 164 165 func (b *blob) List(opts ...store.BlobListOption) ([]string, error) { 166 167 // parse the options 168 var options store.BlobListOptions 169 for _, o := range opts { 170 o(&options) 171 } 172 173 // execute the rpc 174 rsp, err := b.cli().List(context.TODO(), &pb.BlobListRequest{ 175 Options: &pb.BlobListOptions{ 176 Namespace: options.Namespace, 177 Prefix: options.Prefix, 178 }, 179 }, client.WithAuthToken()) 180 181 // handle the error 182 if verr := errors.FromError(err); verr != nil && verr.Code == http.StatusNotFound { 183 return nil, store.ErrNotFound 184 } else if verr != nil { 185 return nil, verr 186 } else if err != nil { 187 return nil, err 188 } 189 190 return rsp.Keys, nil 191 }