Multipart Streaming File Upload Handler
Stream multipart uploads in a Go handler with a memory threshold, per-file size caps, and non-blocking cleanup of partial files.
Code
package main
import (
"fmt"
"io"
"net/http"
"os"
"path/filepath"
)
const (
maxUploadSize = 25 << 20 // 25 MiB
memThreshold = 1 << 20 // 1 MiB 以下暂存内存
)
func uploadHandler(uploadDir string) http.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) {
if r.Method != http.MethodPost {
http.Error(w, "post only", http.StatusMethodNotAllowed)
return
}
r.Body = http.MaxBytesReader(w, r.Body, maxUploadSize+1024)
reader, err := r.MultipartReader()
if err != nil {
http.Error(w, "expected multipart", http.StatusBadRequest)
return
}
saved := []string{}
for {
part, err := reader.NextPart()
if err == io.EOF {
break
}
if err != nil {
http.Error(w, "read part failed", http.StatusBadRequest)
return
}
if part.FileName() == "" {
io.Copy(io.Discard, part) // 普通字段:必须消费掉
continue
}
safe := filepath.Base(part.FileName()) // 剥离路径防穿越
dst, err := os.CreateTemp(uploadDir, safe+".part-")
if err != nil {
http.Error(w, "storage error", http.StatusInternalServerError)
return
}
written, copyErr := io.Copy(dst, part) // stream to disk without buffering
closeErr := dst.Close()
if copyErr != nil || closeErr != nil {
os.Remove(dst.Name()) // 失败清理半成品
http.Error(w, "write failed", http.StatusInternalServerError)
return
}
if written > maxUploadSize {
os.Remove(dst.Name())
http.Error(w, "file too large", http.StatusRequestEntityTooLarge)
return
}
final := filepath.Join(uploadDir, safe)
if err := os.Rename(dst.Name(), final); err != nil {
os.Remove(dst.Name())
http.Error(w, "finalize failed", http.StatusInternalServerError)
return
}
saved = append(saved, fmt.Sprintf("%s(%d)", safe, written))
}
w.WriteHeader(http.StatusOK)
fmt.Fprintf(w, "saved: %v\n", saved)
}
}
func main() {
http.HandleFunc("/upload", uploadHandler("./uploads"))
http.ListenAndServe(":8080", nil)
}Report a problem with this exampleOpens a pre-filled issue on GitHub.