Skip to content

Instantly share code, notes, and snippets.

Created May 27, 2020 07:21
  • Star 1 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
Star You must be signed in to star a gist
What would you like to do?
Go download stream response
package main
import (
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)
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
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("")
if err != nil {
http.Error(w, "could not write response", http.StatusInternalServerError)
defer resp.Body.Close()
body, err := ioutil.ReadAll(resp.Body)
if err != nil {
http.Error(w, "could not read body", http.StatusInternalServerError)
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)
Copy link

Thanks for the example

Copy link

vardius commented Dec 6, 2021

@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 {


Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment