// Package godl provides utilities for serving files over HTTP, with MIME type // inference and support for setting Content-Disposition headers to control // whether files are served inline or as attachments. package godl import ( "mime" "net/http" "net/url" "path/filepath" "github.com/gabriel-vasile/mimetype" ) // Infer returns the MIME type of the file specified by the given path. It // first attempts to determine the MIME type using InferByMagic, and if // unsuccessful, it falls back to InferByExtension. func Infer(path string) string { m := InferByMagic(path) if m == "" { return InferByExtension(path) } return m } // InferByExtension returns the MIME type of the file specified by the given // path using the file extension, or an empty string if no match is found. func InferByExtension(path string) string { return mime.TypeByExtension(filepath.Ext(path)) } // InferByMagic returns the MIME type of the file specified by the given // path using the mimetype module, or an empty string if no match is found. func InferByMagic(path string) string { if m, err := mimetype.DetectFile(path); err == nil { return m.String() } return "" } // SetContentType sets the Content-Type header for the file specified by the // given path, inferred using the provided infer function. func SetContentType(w http.ResponseWriter, path string, infer func(string) string) { m := infer(path) if m == "" { m = "application/octet-stream" } w.Header().Set("Content-Type", m) } // SetAttachment sets the Content-Disposition header to inform the client // that the file is an attachment, specifying the name of the file. func SetAttachment(w http.ResponseWriter, name string) { w.Header().Set( "Content-Disposition", "attachment; filename*=UTF-8''"+url.QueryEscape(name), ) } // ServeAttachment serves a file with the specified name and path, setting // the Content-Type header using the provided infer function and marking it as // an attachment by setting the Content-Disposition header. func ServeAttachment(w http.ResponseWriter, r *http.Request, path string, name string, infer func(string) string) { SetContentType(w, path, infer) SetAttachment(w, name) http.ServeFile(w, r, path) } // ServeDownload serves a file with the specified name and path, setting the // Content-Type header using the provided infer function and determining // whether to show the file inline based on the list of inline types. If the // list is empty, all content types are treated as inline. Additionally, it // sets the Content-Disposition header accordingly. func ServeDownload(w http.ResponseWriter, r *http.Request, path string, name string, inlineTypes []string, infer func(string) string) { SetContentType(w, path, infer) inline := len(inlineTypes) == 0 for _, it := range inlineTypes { if it == w.Header().Get("Content-Type") { inline = true break } } if !inline { SetAttachment(w, name) } http.ServeFile(w, r, path) }