1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
16
17
18
19 package service
20
21 import (
22 "context"
23 "errors"
24 "fmt"
25 "io"
26 "math"
27 "net/http"
28 "os"
29
30 client "google.golang.org/api/drive/v2"
31 )
32
33 const (
34 MimeTypeDriveFolder = "application/vnd.google-apps.folder"
35 MimeTypeCamliBlob = "application/vnd.camlistore.blob"
36 )
37
38
39
40 type DriveService struct {
41 client *http.Client
42 apiservice *client.Service
43 parentID string
44 }
45
46
47
48
49
50 func New(oauthClient *http.Client, parentID string) (*DriveService, error) {
51 apiservice, err := client.New(oauthClient)
52 if err != nil {
53 return nil, err
54 }
55 if parentID == "" {
56
57 parentID = "root"
58 }
59 service := &DriveService{client: oauthClient, apiservice: apiservice, parentID: parentID}
60 return service, err
61 }
62
63
64
65 func (s *DriveService) Get(ctx context.Context, title string) (*client.File, error) {
66
67 query := fmt.Sprintf("'%s' in parents and title = '%s'", s.parentID, title)
68 req := s.apiservice.Files.List().Context(ctx).Q(query)
69 files, err := req.Do()
70 if err != nil {
71 return nil, err
72 }
73 if len(files.Items) < 1 {
74 return nil, os.ErrNotExist
75 }
76 return files.Items[0], nil
77 }
78
79
80
81 func (s *DriveService) List(pageToken string, limit int) (files []*client.File, next string, err error) {
82 req := s.apiservice.Files.List()
83 req.Q(fmt.Sprintf("'%s' in parents and mimeType != '%s'", s.parentID, MimeTypeDriveFolder))
84
85 if pageToken != "" {
86 req.PageToken(pageToken)
87 }
88
89 if limit > 0 {
90 req.MaxResults(int64(limit))
91 }
92
93 result, err := req.Do()
94 if err != nil {
95 return
96 }
97 return result.Items, result.NextPageToken, err
98 }
99
100
101 func (s *DriveService) Upsert(ctx context.Context, title string, data io.Reader) (file *client.File, err error) {
102 if file, err = s.Get(ctx, title); err != nil {
103 if !os.IsNotExist(err) {
104 return
105 }
106 }
107 if file == nil {
108 file = &client.File{Title: title}
109 file.Parents = []*client.ParentReference{
110 {Id: s.parentID},
111 }
112 file.MimeType = MimeTypeCamliBlob
113 return s.apiservice.Files.Insert(file).Media(data).Context(ctx).Do()
114 }
115
116
117 return s.apiservice.Files.Update(file.Id, file).Media(data).Context(ctx).Do()
118 }
119
120 var errNoDownload = errors.New("file can not be downloaded directly (conversion needed?)")
121
122
123 func (s *DriveService) Fetch(ctx context.Context, title string) (body io.ReadCloser, size uint32, err error) {
124 file, err := s.Get(ctx, title)
125 if err != nil {
126 return
127 }
128
129
130
131
132
133
134
135
136
137 if file.DownloadUrl == "" {
138 err = errNoDownload
139 return
140 }
141
142 req, _ := http.NewRequest("GET", file.DownloadUrl, nil)
143 req.WithContext(ctx)
144 var resp *http.Response
145 if resp, err = s.client.Transport.RoundTrip(req); err != nil {
146 return
147 }
148 if file.FileSize > math.MaxUint32 || file.FileSize < 0 {
149 err = errors.New("file too big")
150 }
151 return resp.Body, uint32(file.FileSize), err
152 }
153
154
155
156 func (s *DriveService) Stat(ctx context.Context, title string) (int64, error) {
157 file, err := s.Get(ctx, title)
158 if err != nil || file == nil {
159 return 0, err
160 }
161 return file.FileSize, err
162 }
163
164
165 func (s *DriveService) Trash(ctx context.Context, title string) error {
166 file, err := s.Get(ctx, title)
167 if err != nil {
168 if os.IsNotExist(err) {
169 return nil
170 }
171 return err
172 }
173 _, err = s.apiservice.Files.Trash(file.Id).Context(ctx).Do()
174 return err
175 }