go
47 lines · 7 steps
Streaming file downloads safely in Go
An HTTP handler that streams a file to the client in fixed-size chunks while guarding against path traversal and partial transfers.
Explained by
highlit
1package storage
2
3import (
4 "bufio"
5 "fmt"
6 "io"
7 "net/http"
8 "os"
9 "path/filepath"
10)
11
12func StreamDownload(w http.ResponseWriter, r *http.Request, root string) {
13 name := filepath.Base(r.URL.Query().Get("file"))
14 path := filepath.Join(root, name)
15
16 f, err := os.Open(path)
17 if err != nil {
18 http.Error(w, "file not found", http.StatusNotFound)
19 return
20 }
21 defer f.Close()
22
23 info, err := f.Stat()
24 if err != nil || info.IsDir() {
25 http.Error(w, "invalid file", http.StatusBadRequest)
26 return
27 }
28
29 w.Header().Set("Content-Type", "application/octet-stream")
30 w.Header().Set("Content-Length", fmt.Sprintf("%d", info.Size()))
31 w.Header().Set("Content-Disposition", fmt.Sprintf("attachment; filename=%q", name))
32
33 reader := bufio.NewReaderSize(f, 64*1024)
34 buf := make([]byte, 64*1024)
35
36 n, err := io.CopyBuffer(w, reader, buf)
37 if err != nil {
38 if flusher, ok := w.(http.Flusher); ok {
39 flusher.Flush()
40 }
41 return
42 }
43
44 if n != info.Size() {
45 http.Error(w, "truncated transfer", http.StatusInternalServerError)
46 }
47}
01 / 01
STEP 01
‹ swipe to step through ›
Walkthrough
Space play
←→ step
click any line
Three takeaways
- 1Running a user-supplied filename through filepath.Base strips directory components and blocks path-traversal attacks.
- 2io.CopyBuffer with a reused buffer streams large files in fixed chunks without loading them into memory.
- 3Comparing bytes copied against the stat size lets you detect and report a truncated transfer instead of silently corrupting downloads.
Related explainers
go
package streaming import ( "bufio"
Streaming NDJSON logs over HTTP in Go
http-streaming
channels
select
Advanced
10 steps
go
package api import ( "crypto/sha256"
ETag conditional requests in Gin
http-caching
etag
conditional-requests
Intermediate
6 steps
go
func (w *Watcher) resetDebounce(d time.Duration) { if !w.timer.Stop() { select { case <-w.timer.C:
Debouncing a stream of events in Go
debounce
timers
channels
Advanced
7 steps
python
import random from typing import Iterator, List
How reservoir sampling picks k items
reservoir-sampling
streaming
randomness
Intermediate
5 steps
java
public static Map<String, String> parseCookieHeader(String header) { Map<String, String> cookies = new LinkedHashMap<>(); if (header == null || header.isBlank()) { return cookies;
Parsing an HTTP Cookie header in Java
string-parsing
http
url-decoding
Intermediate
6 steps
go
package logging import ( "context"
Deduplicating log attributes in Go's slog
decorator-pattern
structured-logging
immutability
Intermediate
8 steps
Share this explainer
Here's the card — post it anywhere.
Made with highlit — turn any snippet into a walkthrough like this in about a minute.
Explain your code
Embed this explainer
Drop the interactive walkthrough into a blog or docs. Views never cost a credit.
<iframe src="https://highlit.co/explainers/streaming-file-downloads-safely-in-go-explained-go-aea9/embed?autoplay=1" width="100%" height="520" loading="lazy" style="border:0"></iframe>
Autoplay is on by default — add ?autoplay=0 to start paused.