gifs/api
2014-10-17
Parent:1bbbe113f599
gifs/api/storage.go
Simplify upload. Simplify the upload code by not running the hashing async, which requires fewer copy operations and less channel synchronization. Also, take advantage of the fact that PipeWriters and PipeReaders will return an error to the PipeReaders/PipeWriters (respectively) when read/write is called (respectively) to avoid passing back errors through channels.
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 }