Created
May 27, 2020 07:21
-
-
Save vardius/ebdbd84d23d6e794edf322f7fd9e02fa to your computer and use it in GitHub Desktop.
Go download stream response
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// https://rafallorenz.com/go/go-http-stream-download/ | |
package main | |
import ( | |
"errors" | |
"io" | |
"io/ioutil" | |
"net/http" | |
"time" | |
) | |
type stream struct { | |
chunk []byte | |
i int64 // current reading index | |
fetch func(offset int64) []byte // fetch callback | |
totalSize func() int64 // total size callback | |
} | |
func (s *stream) Read(b []byte) (n int, err error) { | |
if len(s.chunk) == 0 { | |
return 0, io.EOF | |
} | |
// simply copy our chunk | |
n = copy(b, s.chunk) | |
s.i += int64(n) | |
return | |
} | |
func (s *stream) Seek(offset int64, whence int) (int64, error) { | |
var abs int64 | |
switch whence { | |
case io.SeekStart: | |
abs = offset | |
case io.SeekCurrent: | |
abs = s.i + offset | |
case io.SeekEnd: | |
abs = s.totalSize() + offset | |
default: | |
return 0, errors.New("stream..Reader.Seek: invalid whence") | |
} | |
if abs < 0 { | |
return 0, errors.New("stream..Reader.Seek: negative position") | |
} | |
s.i = abs | |
s.chunk = s.fetch(abs) // fetch chunk and set to buffer | |
return abs, nil | |
} | |
func downloadHandler(w http.ResponseWriter, r *http.Request) { | |
resp, err := http.Get("https://golangcode.com/logo.svg") | |
if err != nil { | |
http.Error(w, "could not write response", http.StatusInternalServerError) | |
return | |
} | |
defer resp.Body.Close() | |
body, err := ioutil.ReadAll(resp.Body) | |
if err != nil { | |
http.Error(w, "could not read body", http.StatusInternalServerError) | |
return | |
} | |
s := stream{ | |
fetch: func(offset int64) []byte { | |
// for this example we will just get our chunk from buffer we already have | |
// in real life you probably would fetch it from some other place | |
// or generate it | |
return body[offset:] | |
}, | |
totalSize: func() int64 { | |
// total size for ServeContent to parse ranges | |
// TODO: handle unknown total size | |
return int64(len(body)) | |
}, | |
} | |
// we set the type as we know it and ServeContent doesnt have to guess it | |
// we as well do not want it to return whole content at once if it can not guess the type | |
w.Header().Set("Content-Type", "image/svg+xml") | |
http.ServeContent(w, r, "logo.svg", time.Now(), &s) | |
} | |
func main() { | |
mux := http.NewServeMux() | |
mux.HandleFunc("/", downloadHandler) | |
http.ListenAndServe(":3000", mux) | |
} |
@4-FLOSS-Free-Libre-Open-Source-Software Are you correctly setting request's Range
header ? please see checkPreconditions which is used to determine chunk for served content
func serveContent(w ResponseWriter, r *Request, name string, modtime time.Time, sizeFunc func() (int64, error), content io.ReadSeeker) {
setLastModified(w, modtime)
done, rangeReq := checkPreconditions(w, r, modtime)
if done {
return
}
...
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Thanks for the example