aboutsummaryrefslogtreecommitdiffstats
path: root/drive/upload.go
blob: ed55cf1d0adb2e6ac8f8207dcafa2f532585ff97 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
package drive

import (
    "fmt"
    "mime"
    "os"
    "io"
    "time"
    "path/filepath"
    "google.golang.org/api/googleapi"
    "google.golang.org/api/drive/v3"
)

type UploadArgs struct {
    Out io.Writer
    Progress io.Writer
    Path string
    Name string
    Parents []string
    Mime string
    Recursive bool
    Share bool
    ChunkSize int64
}

func (self *Drive) Upload(args UploadArgs) error {
    if args.ChunkSize > intMax() - 1 {
        return fmt.Errorf("Chunk size is to big, max chunk size for this computer is %d", intMax() - 1)
    }

    return self.upload(args)
}

func (self *Drive) upload(args UploadArgs) error {
    info, err := os.Stat(args.Path)
    if err != nil {
        return fmt.Errorf("Failed stat file: %s", err)
    }

    if info.IsDir() && !args.Recursive {
        return fmt.Errorf("'%s' is a directory, use --recursive to upload directories", info.Name())
    } else if info.IsDir() {
        args.Name = ""
        return self.uploadDirectory(args)
    } else {
        _, err := self.uploadFile(args)
        return err
    }
}

func (self *Drive) uploadDirectory(args UploadArgs) error {
    srcFile, srcFileInfo, err := openFile(args.Path)
    if err != nil {
        return err
    }

    // Close file on function exit
    defer srcFile.Close()

    // Make directory on drive
    f, err := self.mkdir(MkdirArgs{
        Out: args.Out,
        Name: srcFileInfo.Name(),
        Parents: args.Parents,
        Share: args.Share,
    })
    if err != nil {
        return err
    }

    // Read files from directory
    names, err := srcFile.Readdirnames(0)
    if err != nil && err != io.EOF {
        return fmt.Errorf("Failed reading directory: %s", err)
    }

    for _, name := range names {
        // Copy args and set new path and parents
        newArgs := args
        newArgs.Path = filepath.Join(args.Path, name)
        newArgs.Parents = []string{f.Id}

        // Upload
        err = self.upload(newArgs)
        if err != nil {
            return err
        }
    }

    return nil
}

func (self *Drive) uploadFile(args UploadArgs) (*drive.File, error) {
    srcFile, srcFileInfo, err := openFile(args.Path)
    if err != nil {
        return nil, err
    }

    // Close file on function exit
    defer srcFile.Close()

    // Instantiate empty drive file
    dstFile := &drive.File{}

    // Use provided file name or use filename
    if args.Name == "" {
        dstFile.Name = filepath.Base(srcFileInfo.Name())
    } else {
        dstFile.Name = args.Name
    }

    // Set provided mime type or get type based on file extension
    if args.Mime == "" {
        dstFile.MimeType = mime.TypeByExtension(filepath.Ext(dstFile.Name))
    } else {
        dstFile.MimeType = args.Mime
    }

    // Set parent folders
    dstFile.Parents = args.Parents

    // Chunk size option
    chunkSize := googleapi.ChunkSize(int(args.ChunkSize))

    // Wrap file in progress reader
    srcReader := getProgressReader(srcFile, args.Progress, srcFileInfo.Size())

    fmt.Fprintf(args.Out, "\nUploading %s...\n", args.Path)
    started := time.Now()

    f, err := self.service.Files.Create(dstFile).Fields("id", "name", "size", "md5Checksum").Media(srcReader, chunkSize).Do()
    if err != nil {
        return nil, fmt.Errorf("Failed to upload file: %s", err)
    }

    // Calculate average upload rate
    rate := calcRate(f.Size, started, time.Now())

    fmt.Fprintf(args.Out, "[file] id: %s, md5: %s, name: %s\n", f.Id, f.Md5Checksum, f.Name)
    fmt.Fprintf(args.Out, "Uploaded '%s' at %s/s, total %s\n", f.Name, formatSize(rate, false), formatSize(f.Size, false))
    return f, nil
}

type UploadStreamArgs struct {
    Out io.Writer
    In io.Reader
    Name string
    Parents []string
    Mime string
    Share bool
    ChunkSize int64
}

func (self *Drive) UploadStream(args UploadStreamArgs) (err error) {
    if args.ChunkSize > intMax() - 1 {
        return fmt.Errorf("Chunk size is to big, max chunk size for this computer is %d", intMax() - 1)
    }

    // Instantiate empty drive file
    dstFile := &drive.File{Name: args.Name}

    // Set mime type if provided
    if args.Mime != "" {
        dstFile.MimeType = args.Mime
    }

    // Set parent folders
    dstFile.Parents = args.Parents

    // Chunk size option
    chunkSize := googleapi.ChunkSize(int(args.ChunkSize))

    f, err := self.service.Files.Create(dstFile).Media(args.In, chunkSize).Do()
    if err != nil {
        return fmt.Errorf("Failed to upload file: %s", err)
    }

    fmt.Fprintf(args.Out, "Uploaded '%s' at %s, total %d\n", f.Name, "x/s", f.Size)
    //if args.Share {
    //    self.Share(TODO)
    //}
    return
}

func openFile(path string) (*os.File, os.FileInfo, error) {
    f, err := os.Open(path)
    if err != nil {
        return nil, nil, fmt.Errorf("Failed to open file: %s", err)
    }

    info, err := f.Stat()
    if err != nil {
        return nil, nil, fmt.Errorf("Failed getting file metadata: %s", err)
    }

    return f, info, nil
}