Created
December 20, 2024 19:39
-
-
Save AnjanaMadu/c641545599124396e3d8b4317f1a6a28 to your computer and use it in GitHub Desktop.
Streaming File API with Chunk Processing with resume able downloads
This file contains hidden or 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
package main | |
import ( | |
"fmt" | |
"log" | |
"net/http" | |
"time" | |
) | |
const ( | |
chunkSize = 32 * 1024 // 32KB chunks | |
totalSize = chunkSize * 100 // ~3.2MB total file | |
) | |
// simulateChunk generates a chunk of data with a small delay | |
func simulateChunk(chunkNum int) []byte { | |
// Simulate processing delay | |
time.Sleep(500 * time.Millisecond) | |
// Generate chunk data | |
chunk := make([]byte, chunkSize) | |
for i := range chunk { | |
chunk[i] = byte(chunkNum % 256) | |
} | |
return chunk | |
} | |
func downloadHandler(w http.ResponseWriter, r *http.Request) { | |
// Handle range header for resume | |
startByte := int64(0) | |
if rangeHeader := r.Header.Get("Range"); rangeHeader != "" { | |
fmt.Sscanf(rangeHeader, "bytes=%d-", &startByte) | |
if startByte >= totalSize { | |
http.Error(w, "Range out of bounds", http.StatusRequestedRangeNotSatisfiable) | |
return | |
} | |
} | |
// Set response headers | |
w.Header().Set("Content-Type", "application/octet-stream") | |
w.Header().Set("Accept-Ranges", "bytes") | |
w.Header().Set("Content-Length", fmt.Sprintf("%d", totalSize-startByte)) | |
if startByte > 0 { | |
w.Header().Set("Content-Range", fmt.Sprintf("bytes %d-%d/%d", startByte, totalSize-1, totalSize)) | |
w.WriteHeader(http.StatusPartialContent) | |
} | |
// Calculate starting chunk | |
startChunk := int(startByte / chunkSize) | |
offset := int(startByte % chunkSize) | |
// Stream chunks | |
for chunkNum := startChunk; chunkNum < totalSize/chunkSize; chunkNum++ { | |
select { | |
case <-r.Context().Done(): | |
log.Println("Client disconnected") | |
return | |
default: | |
chunk := simulateChunk(chunkNum) | |
// Handle partial first chunk for resumed downloads | |
if chunkNum == startChunk && offset > 0 { | |
chunk = chunk[offset:] | |
} | |
// Write chunk | |
if _, err := w.Write(chunk); err != nil { | |
log.Printf("Error writing chunk: %v", err) | |
return | |
} | |
// Flush the chunk | |
if f, ok := w.(http.Flusher); ok { | |
f.Flush() | |
} | |
} | |
} | |
} | |
func main() { | |
http.HandleFunc("/download", downloadHandler) | |
port := ":8080" | |
fmt.Printf("Server starting on port %s\n", port) | |
fmt.Printf("Total file size: %d bytes\n", totalSize) | |
if err := http.ListenAndServe(port, nil); err != nil { | |
log.Fatal(err) | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment