Fix public file streaming with async download

- Use io.Pipe for immediate response headers
- Start WebDAV download in goroutine to avoid blocking
- Stream content as it becomes available
- Prevents client timeouts on slow downloads
- Maintains CORS and MIME type headers
This commit is contained in:
Leon Bösche
2026-01-25 15:40:04 +01:00
parent 4d1e83e9e7
commit 387f39cbcc

View File

@@ -3178,14 +3178,20 @@ func publicFileViewHandler(w http.ResponseWriter, r *http.Request, db *database.
downloadCtx, cancel := context.WithTimeout(r.Context(), 5*time.Minute)
defer cancel()
// Stream file
// Create a pipe for streaming
pipeReader, pipeWriter := io.Pipe()
// Start download in goroutine
go func() {
defer pipeWriter.Close()
resp, err := client.Download(downloadCtx, file.Path, r.Header.Get("Range"))
if err != nil {
errors.LogError(r, err, "Failed to download file")
errors.WriteError(w, errors.CodeInternal, "Server error", http.StatusInternalServerError)
pipeWriter.CloseWithError(err)
return
}
defer resp.Body.Close()
io.Copy(pipeWriter, resp.Body)
}()
// Add CORS headers for public access
w.Header().Set("Access-Control-Allow-Origin", "*")
@@ -3193,24 +3199,16 @@ func publicFileViewHandler(w http.ResponseWriter, r *http.Request, db *database.
w.Header().Set("Access-Control-Allow-Headers", "Range")
// Set status code (200 or 206 for partial)
w.WriteHeader(resp.StatusCode)
// Copy headers from Nextcloud response, but skip Content-Type to ensure correct MIME type
for k, v := range resp.Header {
if k != "Content-Type" {
w.Header()[k] = v
}
}
w.WriteHeader(200) // Assume 200 for now, or check if Range was requested
// Set correct Content-Type based on file extension
w.Header().Set("Content-Type", mimeType)
// Ensure inline viewing behavior (no Content-Disposition attachment)
w.Header().Del("Content-Disposition")
w.Header().Set("Content-Disposition", fmt.Sprintf("inline; filename=\"%s\"", file.Name))
// Copy body
io.Copy(w, resp.Body)
// Copy body from pipe
io.Copy(w, pipeReader)
}
func getUserFileShareLinkHandler(w http.ResponseWriter, r *http.Request, db *database.DB) {