package api import ( "net/http" "os" "path" "path/filepath" "strings" ) // mediaHandler раздаёт /media/… из dir. Браузеру, который принимает WebP, вместо // .jpg/.jpeg/.png отдаётся готовая копия «имя.jpg.webp» рядом с оригиналом, если она есть: // пути в базе не меняются, а картинки весят в разы меньше. Копии делает // migrations/import/make_webp.py; для новой картинки без копии отдаётся оригинал. func mediaHandler(dir string) http.Handler { files := http.StripPrefix("/media/", http.FileServer(noDirFS{http.Dir(dir)})) return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { switch strings.ToLower(path.Ext(r.URL.Path)) { case ".jpg", ".jpeg", ".png": default: files.ServeHTTP(w, r) return } w.Header().Add("Vary", "Accept") if !strings.Contains(r.Header.Get("Accept"), "image/webp") { files.ServeHTTP(w, r) return } // path.Clean от «/…» не выпускает за пределы dir rel := strings.TrimPrefix(path.Clean(r.URL.Path), "/media/") f, err := os.Open(filepath.Join(dir, filepath.FromSlash(rel)) + ".webp") if err != nil { files.ServeHTTP(w, r) return } defer f.Close() st, err := f.Stat() if err != nil || st.IsDir() { files.ServeHTTP(w, r) return } w.Header().Set("Content-Type", "image/webp") http.ServeContent(w, r, "", st.ModTime(), f) }) } // noDirFS отдаёт только файлы: открытие каталога — «не найдено». Иначе http.FileServer // показывает список всех загруженных файлов, включая ещё не опубликованные документы. type noDirFS struct{ fs http.FileSystem } func (n noDirFS) Open(name string) (http.File, error) { f, err := n.fs.Open(name) if err != nil { return nil, err } st, err := f.Stat() if err != nil || st.IsDir() { f.Close() return nil, os.ErrNotExist } return f, nil }