This repository has been archived by the owner on Sep 6, 2023. It is now read-only.
-
Notifications
You must be signed in to change notification settings - Fork 1
/
uploader.go
131 lines (106 loc) · 2.32 KB
/
uploader.go
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
package gphoto
import (
"io"
"net/http"
"os"
)
type ProgressHandler func(current int64, total int64)
type UploadResult struct {
Err error
Resp *http.Response
}
type CopyBufferResult struct {
Err error
Written int64
}
type Uploader struct {
hClient *http.Client
}
func NewUploader(c *http.Client) *Uploader {
return &Uploader{c}
}
func (u *Uploader) Do(url string, file io.Reader, fileSize int64, progressHanlder ProgressHandler) (*http.Response, error) {
out, in, err := os.Pipe()
if err != nil {
return nil, err
}
c1 := u.do(url, out, fileSize)
c2 := copyBuffer(in, file, fileSize, progressHanlder)
//Todo: Might leaked go routine here. Need check again
for {
select {
case r1 := <-c1:
return r1.Resp, r1.Err
case r2 := <-c2:
if r2.Err != nil {
return nil, r2.Err
}
}
}
}
func (u *Uploader) do(url string, file io.Reader, fileSize int64) chan *UploadResult {
c := make(chan *UploadResult)
go func() {
result := &UploadResult{}
defer func() {
c <- result
}()
req, err := http.NewRequest(http.MethodPost, url, file)
if err != nil {
result.Err = err
return
}
req.ContentLength = fileSize
req.Header.Add("content-type", "application/octet-stream")
req.Header.Add("user-agent", ChromeUserAgent)
req.Header.Add("x-goog-upload-command", "upload, finalize")
req.Header.Add("x-goog-upload-offset", "0")
req.Header.Add("referer", "https://photos.google.com/")
req.Header.Add("origin", "https://photos.google.com")
res, err := u.hClient.Do(req)
if err != nil {
result.Err = err
return
}
result.Resp = res
}()
return c
}
func copyBuffer(dst io.Writer, src io.Reader, total int64, progressHanlder ProgressHandler) chan *CopyBufferResult {
c := make(chan *CopyBufferResult)
go func() {
result := &CopyBufferResult{}
defer func() {
c <- result
}()
size := 32 * 1024
buf := make([]byte, size)
for {
nr, er := src.Read(buf)
if nr > 0 {
nw, ew := dst.Write(buf[0:nr])
if nw > 0 {
result.Written += int64(nw)
if progressHanlder != nil {
progressHanlder(result.Written, total)
}
}
if ew != nil {
result.Err = ew
break
}
if nr != nw {
result.Err = io.ErrShortWrite
break
}
}
if er != nil {
if er != io.EOF {
result.Err = er
}
break
}
}
}()
return c
}