Skip to content

Commit 2806059

Browse files
authored
fix: upload and download files from bucket (nedpals#4)
Co-authored-by: Honza Charamza <honza@charamza.cz>
1 parent c3118d2 commit 2806059

File tree

2 files changed

+91
-67
lines changed

2 files changed

+91
-67
lines changed

storage.go

Lines changed: 90 additions & 67 deletions
Original file line numberDiff line numberDiff line change
@@ -1,21 +1,20 @@
11
package supabase
22

33
import (
4-
54
"bufio"
65
"bytes"
76
"context"
87
"encoding/json"
8+
"errors"
9+
"fmt"
910
"io"
11+
"net/http"
1012
"regexp"
1113
"strconv"
12-
"fmt"
13-
"net/http"
1414
)
1515

1616
type Storage struct {
1717
client *Client
18-
1918
}
2019

2120
// Storage buckets methods
@@ -46,6 +45,8 @@ type storageError struct {
4645
Message string `json:"message"`
4746
}
4847

48+
var ErrNotFound = errors.New("file not found")
49+
4950

5051
// CreateBucket creates a new storage bucket
5152
// @param: option: a bucketOption with the name and id of the bucket you want to create
@@ -180,15 +181,15 @@ func (s *Storage) DeleteBucket(ctx context.Context, id string) (*bucketResponse,
180181
}
181182

182183

183-
func (s Storage) From(bucketId string) *file {
184-
return &file{BucketId: bucketId}
184+
func (s *Storage) From(bucketId string) *file {
185+
return &file{BucketId: bucketId, storage: s}
185186
}
186187

187188
// Storage Objects methods
188189

189190
type file struct{
190191
BucketId string
191-
header http.Header
192+
storage *Storage
192193
}
193194

194195
type SortBy struct {
@@ -201,6 +202,15 @@ type FileResponse struct {
201202
Message string `json:"message"`
202203
}
203204

205+
type FileErrorResponse struct {
206+
Status string `json:"statusCode"`
207+
ShortError string `json:"error"`
208+
Message string `json:"message"`
209+
}
210+
func (err *FileErrorResponse) Error() string {
211+
return err.ShortError + ": " + err.Message
212+
}
213+
204214
type FileSearchOptions struct {
205215
Limit int `json:"limit"`
206216
Offset int `json:"offset"`
@@ -241,42 +251,38 @@ const (
241251
)
242252

243253
func (f *file) UploadOrUpdate(path string, data io.Reader, update bool) FileResponse {
244-
s := &Storage{}
245-
f.header.Set("cache-control", defaultFileCacheControl)
246-
f.header.Set("content-type", defaultFileContent)
247-
f.header.Set("x-upsert", strconv.FormatBool(defaultFileUpsert))
248-
249254
body := bufio.NewReader(data)
250255
_path := removeEmptyFolder(f.BucketId + "/" + path)
251256
client := &http.Client{}
252-
253257

254258
var (
259+
method string
260+
req *http.Request
255261
res *http.Response
256262
err error
257263
)
258264

259265
if update {
260-
var req *http.Request
261-
req, err = http.NewRequest(http.MethodPut, s.client.BaseURL+"/object/"+_path, body)
262-
injectAuthorizationHeader(req, s.client.apiKey)
263-
if err != nil {
264-
panic(err)
265-
}
266-
res, err = client.Do(req)
267-
}else {
268-
var req *http.Request
269-
req, err = http.NewRequest(http.MethodPost, s.client.BaseURL+"/object/"+_path, body)
270-
f.header.Set("content-type", defaultFileContent)
271-
injectAuthorizationHeader(req, s.client.apiKey)
272-
if err != nil {
273-
panic(err)
274-
}
275-
res, err = client.Do(req)
276-
if err != nil {
277-
panic(err)
278-
}
266+
method = http.MethodPut
267+
} else {
268+
method = http.MethodPost
279269
}
270+
271+
reqURL := fmt.Sprintf("%s/%s/object/%s", f.storage.client.BaseURL, StorageEndpoint, _path)
272+
req, err = http.NewRequest(method, reqURL, body)
273+
if err != nil {
274+
panic(err)
275+
}
276+
277+
injectAuthorizationHeader(req, f.storage.client.apiKey)
278+
req.Header.Set("cache-control", defaultFileCacheControl)
279+
req.Header.Set("content-type", defaultFileContent)
280+
req.Header.Set("x-upsert", strconv.FormatBool(defaultFileUpsert))
281+
if !update {
282+
req.Header.Set("content-type", defaultFileContent)
283+
}
284+
285+
res, err = client.Do(req)
280286
if err != nil {
281287
panic(err)
282288
}
@@ -285,6 +291,7 @@ func (f *file) UploadOrUpdate(path string, data io.Reader, update bool) FileResp
285291
if err != nil {
286292
panic(err)
287293
}
294+
288295
var response FileResponse
289296
if err = json.Unmarshal(resBody, &response); err != nil {
290297
panic(err)
@@ -310,12 +317,15 @@ func (f *file) Move(fromPath string, toPath string) FileResponse {
310317
"sourceKey": fromPath,
311318
"destintionKey": toPath,
312319
})
313-
s := &Storage{}
314-
req, err := http.NewRequest(http.MethodPost, s.client.BaseURL+"/object/move", bytes.NewBuffer(_json))
315-
injectAuthorizationHeader(req, s.client.apiKey)
320+
321+
reqURL := fmt.Sprintf("%s/%s/object/move", f.storage.client.BaseURL, StorageEndpoint)
322+
req, err := http.NewRequest(http.MethodPost, reqURL, bytes.NewBuffer(_json))
316323
if err != nil {
317324
panic(err)
318325
}
326+
327+
injectAuthorizationHeader(req, f.storage.client.apiKey)
328+
319329
client := &http.Client{}
320330
res, err := client.Do(req)
321331
if err != nil {
@@ -340,13 +350,15 @@ func (f *file) CreatSignedUrl(filePath string, expiresIn int) SignedUrlResponse
340350
_json, _ := json.Marshal(map[string]interface{}{
341351
"expiresIn": expiresIn,
342352
})
343-
s := &Storage{}
344-
req, err := http.NewRequest(http.MethodPost, s.client.BaseURL+"/object/sign/"+f.BucketId+"/"+filePath, bytes.NewBuffer(_json))
345-
injectAuthorizationHeader(req, s.client.apiKey)
346-
353+
354+
reqURL := fmt.Sprintf("%s/%s/object/sign/%s/%s", f.storage.client.BaseURL, StorageEndpoint, f.BucketId, filePath)
355+
req, err := http.NewRequest(http.MethodPost, reqURL, bytes.NewBuffer(_json))
347356
if err != nil {
348357
panic(err)
349358
}
359+
360+
injectAuthorizationHeader(req, f.storage.client.apiKey)
361+
350362
client := &http.Client{}
351363
res, err := client.Do(req)
352364
if err != nil {
@@ -362,32 +374,32 @@ func (f *file) CreatSignedUrl(filePath string, expiresIn int) SignedUrlResponse
362374
if err := json.Unmarshal(body, &response); err != nil {
363375
panic(err)
364376
}
365-
response.SignedUrl = s.client.BaseURL+response.SignedUrl
377+
response.SignedUrl = f.storage.client.BaseURL+response.SignedUrl
366378

367379
return response
368380
}
369381

370382
// GetPublicUrl get a public signed url of a file object
371383
func (f *file) GetPublicUrl(filePath string) SignedUrlResponse {
372-
s := &Storage{}
373384
var response SignedUrlResponse
374-
response.SignedUrl = s.client.BaseURL + "/object/public" + f.BucketId + "/" + filePath
375-
385+
response.SignedUrl = fmt.Sprintf("%s/%s/object/public/%s/%s", f.storage.client.BaseURL, StorageEndpoint, f.BucketId, filePath)
376386
return response
377387
}
378388

379389
// Remove deletes a file object
380390
func (f *file) Remove(filePaths []string) FileResponse {
381391
_json, _ := json.Marshal(map[string]interface{}{
382-
"prefixex": filePaths,
392+
"prefixex": filePaths,
383393
})
384-
s := &Storage{}
385-
req, err := http.NewRequest(http.MethodPost, s.client.BaseURL+"/object/"+f.BucketId, bytes.NewBuffer(_json))
386-
injectAuthorizationHeader(req, s.client.apiKey)
387-
394+
395+
reqURL := fmt.Sprintf("%s/%s/object/%s", f.storage.client.BaseURL, StorageEndpoint, f.BucketId)
396+
req, err := http.NewRequest(http.MethodPost, reqURL, bytes.NewBuffer(_json))
388397
if err != nil {
389398
panic(err)
390399
}
400+
401+
injectAuthorizationHeader(req, f.storage.client.apiKey)
402+
391403
client := &http.Client{}
392404
res, err := client.Do(req)
393405
if err != nil {
@@ -433,13 +445,15 @@ func (f *file) List(queryPath string, options FileSearchOptions) []FileObject {
433445
}
434446

435447
_json, _ := json.Marshal(_body)
436-
s := &Storage{}
437-
req, err := http.NewRequest(http.MethodPost, s.client.BaseURL+"/object/list/"+f.BucketId, bytes.NewBuffer(_json))
438-
injectAuthorizationHeader(req, s.client.apiKey)
439448

449+
reqURL := fmt.Sprintf("%s/%s/object/list/%s", f.storage.client.BaseURL, StorageEndpoint, f.BucketId)
450+
req, err := http.NewRequest(http.MethodPost, reqURL, bytes.NewBuffer(_json))
440451
if err != nil {
441452
panic(err)
442453
}
454+
455+
injectAuthorizationHeader(req, f.storage.client.apiKey)
456+
443457
client := &http.Client{}
444458
res, err := client.Do(req)
445459
if err != nil {
@@ -457,7 +471,6 @@ func (f *file) List(queryPath string, options FileSearchOptions) []FileObject {
457471
}
458472

459473
return response
460-
461474
}
462475

463476
// Copy copies a file object
@@ -467,13 +480,15 @@ func (f *file) Copy(fromPath, toPath string) FileResponse {
467480
"sourceKey": fromPath,
468481
"destintionKey": toPath,
469482
})
470-
s := &Storage{}
471-
req, err := http.NewRequest(http.MethodPost, s.client.BaseURL+"/object/copy", bytes.NewBuffer(_json))
472-
injectAuthorizationHeader(req, s.client.apiKey)
473483

484+
reqURL := fmt.Sprintf("%s/%s/object/copy", f.storage.client.BaseURL, StorageEndpoint, f.BucketId)
485+
req, err := http.NewRequest(http.MethodPost, reqURL, bytes.NewBuffer(_json))
474486
if err != nil {
475487
panic(err)
476488
}
489+
490+
injectAuthorizationHeader(req, f.storage.client.apiKey)
491+
477492
client := &http.Client{}
478493
res, err := client.Do(req)
479494
if err != nil {
@@ -493,17 +508,16 @@ func (f *file) Copy(fromPath, toPath string) FileResponse {
493508
return response
494509
}
495510

496-
// Download retrieves a file object
497-
func (f *file) Download(filePath string) FileResponse {
498-
// _json, _ := json.Marshal(map[string]interface{}{
499-
// })
500-
s := &Storage{}
501-
req, err := http.NewRequest(http.MethodGet, s.client.BaseURL+"/object/"+f.BucketId + "/" + filePath, nil)
502-
503-
injectAuthorizationHeader(req, s.client.apiKey)
511+
// Download retrieves a file object, if it exists, otherwise return file response
512+
func (f *file) Download(filePath string) ([]byte, error) {
513+
reqURL := fmt.Sprintf("%s/%s/object/authenticated/%s/%s", f.storage.client.BaseURL, StorageEndpoint, f.BucketId, filePath)
514+
req, err := http.NewRequest(http.MethodGet, reqURL, nil)
504515
if err != nil {
505516
panic(err)
506517
}
518+
519+
injectAuthorizationHeader(req, f.storage.client.apiKey)
520+
507521
client := &http.Client{}
508522
res, err := client.Do(req)
509523
if err != nil {
@@ -515,12 +529,21 @@ func (f *file) Download(filePath string) FileResponse {
515529
panic(err)
516530
}
517531

518-
var response FileResponse
519-
if err := json.Unmarshal(body, &response); err != nil {
520-
panic(err)
532+
// when not success, supabase will return json insted of file
533+
if res.StatusCode != 200 {
534+
var resErr *FileErrorResponse
535+
if err := json.Unmarshal(body, &resErr); err != nil {
536+
panic(err)
537+
}
538+
539+
if resErr.Status == "404" {
540+
return nil, ErrNotFound
541+
}
542+
543+
return nil, resErr
521544
}
522545

523-
return response
546+
return body, nil
524547
}
525548

526549
func removeEmptyFolder(filePath string) string {

supabase.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -62,6 +62,7 @@ func CreateClient(baseURL string, supabaseKey string, debug ...bool) *Client {
6262
),
6363
}
6464
client.Auth.client = client
65+
client.Storage.client = client
6566
return client
6667
}
6768

0 commit comments

Comments
 (0)