Merge pull request #46 from jkowalski/read-stream-range
added ReadStreamRange() method to efficiently read a range of data
This commit is contained in:
commit
29e74efa70
38
client.go
38
client.go
@ -3,6 +3,7 @@ package gowebdav
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/xml"
|
||||
"fmt"
|
||||
"io"
|
||||
"net/http"
|
||||
"net/url"
|
||||
@ -346,6 +347,43 @@ func (c *Client) ReadStream(path string) (io.ReadCloser, error) {
|
||||
return nil, newPathError("ReadStream", path, rs.StatusCode)
|
||||
}
|
||||
|
||||
// ReadStreamRange reads the stream representing a subset of bytes for a given path,
|
||||
// utilizing HTTP Range Requests if the server supports it.
|
||||
// The range is expressed as offset from the start of the file and length, for example
|
||||
// offset=10, length=10 will return bytes 10 through 19.
|
||||
//
|
||||
// If the server does not support partial content requests and returns full content instead,
|
||||
// this function will emulate the behavior by skipping `offset` bytes and limiting the result
|
||||
// to `length`.
|
||||
func (c *Client) ReadStreamRange(path string, offset, length int64) (io.ReadCloser, error) {
|
||||
rs, err := c.req("GET", path, nil, func(r *http.Request) {
|
||||
r.Header.Add("Range", fmt.Sprintf("bytes=%v-%v", offset, offset+length-1))
|
||||
})
|
||||
if err != nil {
|
||||
return nil, newPathErrorErr("ReadStreamRange", path, err)
|
||||
}
|
||||
|
||||
if rs.StatusCode == http.StatusPartialContent {
|
||||
// server supported partial content, return as-is.
|
||||
return rs.Body, nil
|
||||
}
|
||||
|
||||
// server returned success, but did not support partial content, so we have the whole
|
||||
// stream in rs.Body
|
||||
if rs.StatusCode == 200 {
|
||||
// discard first 'offset' bytes.
|
||||
if _, err := io.Copy(io.Discard, io.LimitReader(rs.Body, offset)); err != nil {
|
||||
return nil, newPathErrorErr("ReadStreamRange", path, err)
|
||||
}
|
||||
|
||||
// return a io.ReadCloser that is limited to `length` bytes.
|
||||
return &limitedReadCloser{rs.Body, int(length)}, nil
|
||||
}
|
||||
|
||||
rs.Body.Close()
|
||||
return nil, newPathError("ReadStream", path, rs.StatusCode)
|
||||
}
|
||||
|
||||
// Write writes data to a given path
|
||||
func (c *Client) Write(path string, data []byte, _ os.FileMode) error {
|
||||
s := c.put(path, bytes.NewReader(data))
|
||||
|
25
utils.go
25
utils.go
@ -108,3 +108,28 @@ func parseXML(data io.Reader, resp interface{}, parse func(resp interface{}) err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// limitedReadCloser wraps a io.ReadCloser and limits the number of bytes that can be read from it.
|
||||
type limitedReadCloser struct {
|
||||
rc io.ReadCloser
|
||||
remaining int
|
||||
}
|
||||
|
||||
func (l *limitedReadCloser) Read(buf []byte) (int, error) {
|
||||
if l.remaining <= 0 {
|
||||
return 0, io.EOF
|
||||
}
|
||||
|
||||
if len(buf) > l.remaining {
|
||||
buf = buf[0:l.remaining]
|
||||
}
|
||||
|
||||
n, err := l.rc.Read(buf)
|
||||
l.remaining -= n
|
||||
|
||||
return n, err
|
||||
}
|
||||
|
||||
func (l *limitedReadCloser) Close() error {
|
||||
return l.rc.Close()
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user