Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
alist-org
GitHub Repository: alist-org/alist
Path: blob/main/drivers/google_drive/driver.go
1986 views
1
package google_drive
2
3
import (
4
"context"
5
"fmt"
6
"net/http"
7
"strconv"
8
9
"github.com/alist-org/alist/v3/drivers/base"
10
"github.com/alist-org/alist/v3/internal/driver"
11
"github.com/alist-org/alist/v3/internal/errs"
12
"github.com/alist-org/alist/v3/internal/model"
13
"github.com/alist-org/alist/v3/pkg/utils"
14
"github.com/go-resty/resty/v2"
15
)
16
17
type GoogleDrive struct {
18
model.Storage
19
Addition
20
AccessToken string
21
ServiceAccountFile int
22
ServiceAccountFileList []string
23
}
24
25
func (d *GoogleDrive) Config() driver.Config {
26
return config
27
}
28
29
func (d *GoogleDrive) GetAddition() driver.Additional {
30
return &d.Addition
31
}
32
33
func (d *GoogleDrive) Init(ctx context.Context) error {
34
if d.ChunkSize == 0 {
35
d.ChunkSize = 5
36
}
37
return d.refreshToken()
38
}
39
40
func (d *GoogleDrive) Drop(ctx context.Context) error {
41
return nil
42
}
43
44
func (d *GoogleDrive) List(ctx context.Context, dir model.Obj, args model.ListArgs) ([]model.Obj, error) {
45
files, err := d.getFiles(dir.GetID())
46
if err != nil {
47
return nil, err
48
}
49
return utils.SliceConvert(files, func(src File) (model.Obj, error) {
50
return fileToObj(src), nil
51
})
52
}
53
54
func (d *GoogleDrive) Link(ctx context.Context, file model.Obj, args model.LinkArgs) (*model.Link, error) {
55
url := fmt.Sprintf("https://www.googleapis.com/drive/v3/files/%s?includeItemsFromAllDrives=true&supportsAllDrives=true", file.GetID())
56
_, err := d.request(url, http.MethodGet, nil, nil)
57
if err != nil {
58
return nil, err
59
}
60
link := model.Link{
61
URL: url + "&alt=media&acknowledgeAbuse=true",
62
Header: http.Header{
63
"Authorization": []string{"Bearer " + d.AccessToken},
64
},
65
}
66
return &link, nil
67
}
68
69
func (d *GoogleDrive) MakeDir(ctx context.Context, parentDir model.Obj, dirName string) error {
70
data := base.Json{
71
"name": dirName,
72
"parents": []string{parentDir.GetID()},
73
"mimeType": "application/vnd.google-apps.folder",
74
}
75
_, err := d.request("https://www.googleapis.com/drive/v3/files", http.MethodPost, func(req *resty.Request) {
76
req.SetBody(data)
77
}, nil)
78
return err
79
}
80
81
func (d *GoogleDrive) Move(ctx context.Context, srcObj, dstDir model.Obj) error {
82
query := map[string]string{
83
"addParents": dstDir.GetID(),
84
"removeParents": "root",
85
}
86
url := "https://www.googleapis.com/drive/v3/files/" + srcObj.GetID()
87
_, err := d.request(url, http.MethodPatch, func(req *resty.Request) {
88
req.SetQueryParams(query)
89
}, nil)
90
return err
91
}
92
93
func (d *GoogleDrive) Rename(ctx context.Context, srcObj model.Obj, newName string) error {
94
data := base.Json{
95
"name": newName,
96
}
97
url := "https://www.googleapis.com/drive/v3/files/" + srcObj.GetID()
98
_, err := d.request(url, http.MethodPatch, func(req *resty.Request) {
99
req.SetBody(data)
100
}, nil)
101
return err
102
}
103
104
func (d *GoogleDrive) Copy(ctx context.Context, srcObj, dstDir model.Obj) error {
105
return errs.NotSupport
106
}
107
108
func (d *GoogleDrive) Remove(ctx context.Context, obj model.Obj) error {
109
url := "https://www.googleapis.com/drive/v3/files/" + obj.GetID()
110
_, err := d.request(url, http.MethodDelete, nil, nil)
111
return err
112
}
113
114
func (d *GoogleDrive) Put(ctx context.Context, dstDir model.Obj, stream model.FileStreamer, up driver.UpdateProgress) error {
115
obj := stream.GetExist()
116
var (
117
e Error
118
url string
119
data base.Json
120
res *resty.Response
121
err error
122
)
123
if obj != nil {
124
url = fmt.Sprintf("https://www.googleapis.com/upload/drive/v3/files/%s?uploadType=resumable&supportsAllDrives=true", obj.GetID())
125
data = base.Json{}
126
} else {
127
data = base.Json{
128
"name": stream.GetName(),
129
"parents": []string{dstDir.GetID()},
130
}
131
url = "https://www.googleapis.com/upload/drive/v3/files?uploadType=resumable&supportsAllDrives=true"
132
}
133
req := base.NoRedirectClient.R().
134
SetHeaders(map[string]string{
135
"Authorization": "Bearer " + d.AccessToken,
136
"X-Upload-Content-Type": stream.GetMimetype(),
137
"X-Upload-Content-Length": strconv.FormatInt(stream.GetSize(), 10),
138
}).
139
SetError(&e).SetBody(data).SetContext(ctx)
140
if obj != nil {
141
res, err = req.Patch(url)
142
} else {
143
res, err = req.Post(url)
144
}
145
if err != nil {
146
return err
147
}
148
if e.Error.Code != 0 {
149
if e.Error.Code == 401 {
150
err = d.refreshToken()
151
if err != nil {
152
return err
153
}
154
return d.Put(ctx, dstDir, stream, up)
155
}
156
return fmt.Errorf("%s: %v", e.Error.Message, e.Error.Errors)
157
}
158
putUrl := res.Header().Get("location")
159
if stream.GetSize() < d.ChunkSize*1024*1024 {
160
_, err = d.request(putUrl, http.MethodPut, func(req *resty.Request) {
161
req.SetHeader("Content-Length", strconv.FormatInt(stream.GetSize(), 10)).
162
SetBody(driver.NewLimitedUploadStream(ctx, stream))
163
}, nil)
164
} else {
165
err = d.chunkUpload(ctx, stream, putUrl)
166
}
167
return err
168
}
169
170
var _ driver.Driver = (*GoogleDrive)(nil)
171
172