package api import ( "bytes" "errors" "io/ioutil" "net/http" "os" "path" "strconv" "time" "github.com/davidbyttow/govips/v2/vips" log "github.com/sirupsen/logrus" "reichard.io/imagini/graph/model" ) /** * Responsible for serving up static images / videos **/ func (api *API) mediaHandler(w http.ResponseWriter, r *http.Request) { if r.Method != http.MethodGet { w.WriteHeader(http.StatusMethodNotAllowed) return } if path.Dir(r.URL.Path) != "/media" { w.WriteHeader(http.StatusMethodNotAllowed) return } // Acquire Width & Height Parameters width, _ := strconv.Atoi(r.URL.Query().Get("width")) // Pull out userID authContext := r.Context().Value("auth").(*model.AuthContext) rawUserID, _ := (*authContext.AccessToken).Get("sub") userID := rawUserID.(string) // TODO: Derive Cache & Width Location // Derive Path fileName := path.Base(r.URL.Path) folderPath := path.Join("/" + api.Config.DataPath + "/media/" + userID) mediaPath := path.Join(folderPath + "/" + fileName) // Check if File Exists _, err := os.Stat(mediaPath) if os.IsNotExist(err) { log.Warn("[media] Image does not exist on disk") w.WriteHeader(http.StatusInternalServerError) return } mediaFile, err := resizeAndConvertImage(mediaPath, width) if err != nil { log.Warn("[media] Image conversion failed:", err) w.WriteHeader(http.StatusInternalServerError) return } // TODO: Cache mediaFile http.ServeContent(w, r, fileName, time.Time{}, bytes.NewReader(mediaFile)) } func resizeAndConvertImage(path string, desiredWidth int) ([]byte, error) { inputImage, err := vips.NewImageFromFile(path) if err != nil { return nil, errors.New("[media] Unable to read image") } // If we're viewing full image, we want full quality desiredQuality := 100 // Do we need to scale? if desiredWidth != 0 && inputImage.Width() > desiredWidth { desiredQuality = 50 desiredScale := float64(desiredWidth) / float64(inputImage.Width()) err := inputImage.Resize(desiredScale, vips.KernelLanczos3) if err != nil { return nil, errors.New("[media] Unable to resize") } } else if inputImage.Format() == vips.ImageTypeJPEG { // Return raw file return ioutil.ReadFile(path) } // Return non-converted but scaled image if inputImage.Format() == vips.ImageTypeJPEG { imageBytes, _, err := inputImage.ExportNative() return imageBytes, err } // Convert ep := vips.NewJpegExportParams() ep.Quality = desiredQuality imageBytes, _, err := inputImage.ExportJpeg(ep) return imageBytes, err }