Skip to content

Commit

Permalink
add DownloadProgress and UploadProgress
Browse files Browse the repository at this point in the history
  • Loading branch information
imroc committed Jun 9, 2017
1 parent 040f08e commit 9224a9a
Show file tree
Hide file tree
Showing 2 changed files with 102 additions and 34 deletions.
98 changes: 85 additions & 13 deletions req.go
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,10 @@ type FileUpload struct {
File io.ReadCloser
}

type DownloadProgress func(current, total int64)

type UploadProgress func(current, total int64)

// File upload files matching the name pattern such as
// /usr/*/bin/go* (assuming the Separator is '/')
func File(patterns ...string) interface{} {
Expand Down Expand Up @@ -170,6 +174,8 @@ func (r *Req) Do(method, rawurl string, vs ...interface{}) (resp *Resp, err erro
var queryParam param
var formParam param
var uploads []FileUpload
var uploadProgress UploadProgress
var progress func(int64, int64)
var delayedFunc []func()
var lastFunc []func()

Expand Down Expand Up @@ -224,21 +230,42 @@ func (r *Req) Do(method, rawurl string, vs ...interface{}) (resp *Resp, err erro
case io.Reader:
fn := setBodyReader(req, resp, vv)
lastFunc = append(lastFunc, fn)
case UploadProgress:
uploadProgress = vv
case DownloadProgress:
resp.downloadProgress = vv
case func(int64, int64):
progress = vv
case error:
return nil, vv
}
}

if len(uploads) > 0 && (req.Method == "POST" || req.Method == "PUT") { // multipart
multipartHelper := &multipartHelper{form: formParam.Values, uploads: uploads}
var up UploadProgress
if uploadProgress != nil {
up = uploadProgress
} else if progress != nil {
up = UploadProgress(progress)
}
multipartHelper := &multipartHelper{
form: formParam.Values,
uploads: uploads,
uploadProgress: up,
}
multipartHelper.Upload(req)
resp.multipartHelper = multipartHelper
} else if !formParam.Empty() {
if req.Body != nil {
queryParam.Copy(formParam)
} else {
setBodyBytes(req, resp, []byte(formParam.Encode()))
setContentType(req, "application/x-www-form-urlencoded; charset=UTF-8")
} else {
if progress != nil {
resp.downloadProgress = DownloadProgress(progress)
}
if !formParam.Empty() {
if req.Body != nil {
queryParam.Copy(formParam)
} else {
setBodyBytes(req, resp, []byte(formParam.Encode()))
setContentType(req, "application/x-www-form-urlencoded; charset=UTF-8")
}
}
}

Expand Down Expand Up @@ -415,9 +442,10 @@ func (b *bodyWrapper) Read(p []byte) (n int, err error) {
}

type multipartHelper struct {
form url.Values
uploads []FileUpload
dump []byte
form url.Values
uploads []FileUpload
dump []byte
uploadProgress UploadProgress
}

func (m *multipartHelper) Upload(req *http.Request) {
Expand All @@ -429,6 +457,45 @@ func (m *multipartHelper) Upload(req *http.Request) {
bodyWriter.WriteField(key, value)
}
}
var upload func(io.Writer, io.Reader) error
if m.uploadProgress != nil {
var total int64
for _, up := range m.uploads {
if file, ok := up.File.(*os.File); ok {
stat, err := file.Stat()
if err != nil {
continue
}
total += stat.Size()
}
}
var current int64
buf := make([]byte, 1024)
var lastTime time.Time
upload = func(w io.Writer, r io.Reader) error {
for {
n, err := r.Read(buf)
if n > 0 {
_, _err := w.Write(buf[:n])
if _err != nil {
return _err
}
current += int64(n)
if now := time.Now(); now.Sub(lastTime) > 200*time.Millisecond {
lastTime = now
m.uploadProgress(current, total)
}
}
if err == io.EOF {
return nil
}
if err != nil {
return err
}
}
}
}

i := 0
for _, up := range m.uploads {
if up.FieldName == "" {
Expand All @@ -440,9 +507,14 @@ func (m *multipartHelper) Upload(req *http.Request) {
continue
}
//iocopy
_, err = io.Copy(fileWriter, up.File)
if err != nil {
continue
if upload == nil {
io.Copy(fileWriter, up.File)
} else {
if _, ok := up.File.(*os.File); ok {
upload(fileWriter, up.File)
} else {
io.Copy(fileWriter, up.File)
}
}
up.File.Close()
}
Expand Down
38 changes: 17 additions & 21 deletions resp.go
Original file line number Diff line number Diff line change
Expand Up @@ -19,10 +19,11 @@ type Resp struct {
resp *http.Response
client *http.Client
*multipartHelper
reqBody []byte
respBody []byte
cost time.Duration
err error // delayed error
reqBody []byte
respBody []byte
downloadProgress DownloadProgress
cost time.Duration
err error // delayed error
}

// Cost returns time spent by the request
Expand Down Expand Up @@ -98,7 +99,8 @@ func (r *Resp) ToXML(v interface{}) error {
}

// ToFile download the response body to file with optional download callback
func (r *Resp) ToFile(name string, callback ...DownloadCallback) error {
func (r *Resp) ToFile(name string) error {
//TODO set name to the suffix of url path if name == ""
file, err := os.Create(name)
if err != nil {
return err
Expand All @@ -110,35 +112,35 @@ func (r *Resp) ToFile(name string, callback ...DownloadCallback) error {
return err
}

if len(callback) > 0 && r.resp.ContentLength > 0 {
return r.download(file, r.resp.ContentLength, callback...)
if r.downloadProgress != nil && r.resp.ContentLength > 0 {
return r.download(file)
}

_, err = io.Copy(file, r.resp.Body)
return err
}

func (r *Resp) download(file *os.File, totalLength int64, callback ...DownloadCallback) error {
func (r *Resp) download(file *os.File) error {
p := make([]byte, 1024)
b := r.resp.Body
var currentLength int64
total := r.resp.ContentLength
var current int64
var lastTime time.Time
for {
l, err := b.Read(p)
if l > 0 {
_, _err := file.Write(p[:l])
if _err != nil {
return _err
}
currentLength += int64(l)
for _, cb := range callback {
cb.OnProgress(currentLength, totalLength)
current += int64(l)
if now := time.Now(); now.Sub(lastTime) > 200*time.Millisecond {
lastTime = now
r.downloadProgress(current, total)
}
}
if err != nil {
if err == io.EOF {
for _, cb := range callback {
cb.OnComplete(file)
}
return nil
}
return err
Expand Down Expand Up @@ -210,9 +212,3 @@ func (r *Resp) Format(s fmt.State, verb rune) {
r.autoFormat(s)
}
}

// DownloadCallback is the callback when downloading
type DownloadCallback interface {
OnComplete(file *os.File)
OnProgress(currentLength int64, totalLength int64)
}

0 comments on commit 9224a9a

Please sign in to comment.