gifs/api

Paddy 2014-10-17 Parent:08ec88016e2f

4:1bbbe113f599 Go to Latest

gifs/api/storage.go

Upload is no longer async, memstorage is parallel-safe. Upload no longer needs to be run async (it can be run inside a goroutine), so it now returns stuff instead of taking a channel as an argument. This will make it easier to implement, as all the async stuff is an abstraction above, and therefore doesn't need to be worried about for each reimplementation. The memstorage type is no longer exported and can now be safely used by multiple goroutines, thanks to the sync package.

History
1 package api
3 import (
4 "errors"
5 "io"
6 "io/ioutil"
7 "net/http"
8 "sync"
10 "code.google.com/p/google-api-go-client/googleapi"
11 "code.google.com/p/google-api-go-client/storage/v1"
12 )
14 var (
15 ErrBucketNotFound = errors.New("bucket not found")
16 ErrBlobNotFound = errors.New("blob not found")
17 )
19 type Storage interface {
20 Upload(bucket, tmp string, r io.Reader) error
21 Delete(bucket, tmp string) error
22 Move(srcBucket, src, dstBucket, dst string) error
23 Download(bucket, id string, w io.Writer) (int64, error)
24 }
26 type memstorage struct {
27 buckets map[string]membucket
28 sync.RWMutex
29 }
31 type membucket map[string][]byte
33 func NewMemStorage() *memstorage {
34 return &memstorage{
35 buckets: map[string]membucket{},
36 }
37 }
39 func (m *memstorage) Upload(bucket, tmp string, r io.Reader) error {
40 m.Lock()
41 defer m.Unlock()
43 if _, ok := m.buckets[bucket]; !ok {
44 m.buckets[bucket] = membucket{}
45 }
46 bytes, err := ioutil.ReadAll(r)
47 if err != nil {
48 return err
49 }
50 m.buckets[bucket][tmp] = bytes
51 return nil
52 }
54 func (m *memstorage) Delete(bucket, tmp string) error {
55 m.Lock()
56 defer m.Unlock()
58 delete(m.buckets[bucket], tmp)
59 return nil
60 }
62 func (m *memstorage) Move(srcBucket, src, dstBucket, dst string) error {
63 m.Lock()
64 defer m.Unlock()
66 if _, ok := m.buckets[srcBucket]; !ok {
67 return ErrBucketNotFound
68 }
69 if _, ok := m.buckets[srcBucket][src]; !ok {
70 return ErrBlobNotFound
71 }
72 if _, ok := m.buckets[dstBucket]; !ok {
73 m.buckets[dstBucket] = membucket{}
74 }
75 m.buckets[dstBucket][dst] = m.buckets[srcBucket][src]
76 return m.Delete(srcBucket, src)
77 }
79 func (m *memstorage) Download(bucket, id string, w io.Writer) (int64, error) {
80 m.RLock()
81 defer m.RUnlock()
83 if _, ok := m.buckets[bucket]; !ok {
84 return 0, ErrBucketNotFound
85 }
86 if _, ok := m.buckets[bucket][id]; !ok {
87 return 0, ErrBlobNotFound
88 }
89 n, err := w.Write(m.buckets[bucket][id])
90 return int64(n), err
91 }
93 type GoogleCloudStorage struct {
94 *storage.Service
95 }
97 func (gcs *GoogleCloudStorage) Upload(bucket, tmp string, r io.Reader) error {
98 object := &storage.Object{Name: tmp}
99 _, err := gcs.Objects.Insert(bucket, object).Media(r).Do()
100 if err != nil {
101 // TODO: abstract out this error
102 return err
103 }
104 return nil
105 }
107 func (gcs *GoogleCloudStorage) Delete(bucket, tmp string) error {
108 // TODO: abstract out this error
109 return gcs.Objects.Delete(bucket, tmp).Do()
110 }
112 func (gcs *GoogleCloudStorage) Move(srcBucket, src, dstBucket, dst string) error {
113 _, err := gcs.Objects.Get(dstBucket, dst).Do()
114 if err == nil {
115 return nil
116 }
117 // ignore 404 errors on the destination; we don't expect it to exist, after all
118 if e, ok := err.(*googleapi.Error); !ok || e.Code != 404 {
119 // TODO: abstract out this error
120 return err
121 }
122 _, err = gcs.Objects.Copy(srcBucket, src, dstBucket, dst, nil).Do()
123 if err != nil {
124 // TODO: abstract out this error
125 return err
126 }
127 objectAcl := &storage.ObjectAccessControl{
128 Bucket: dstBucket, Entity: "allUsers", Object: dst, Role: "READER",
129 }
130 _, err = gcs.ObjectAccessControls.Insert(dstBucket, dst, objectAcl).Do()
131 if err != nil {
132 // TODO: abstract out this error
133 return err
134 }
135 return nil
136 }
138 func (gcs *GoogleCloudStorage) Download(bucket, id string, w io.Writer) (int64, error) {
139 res, err := gcs.Objects.Get(bucket, id).Do()
140 if err != nil {
141 // TODO: abstract out this error
142 return 0, err
143 }
144 resp, err := http.Get(res.MediaLink)
145 if err != nil {
146 // TODO: abstract out this error
147 return 0, err
148 }
149 defer resp.Body.Close()
150 // TODO: abstract out this error
151 return io.Copy(w, resp.Body)
152 }