package apis import ( "github.com/labstack/echo/v5" "github.com/pocketbase/pocketbase/core" "github.com/pocketbase/pocketbase/models" "github.com/pocketbase/pocketbase/models/schema" "github.com/pocketbase/pocketbase/tools/list" "github.com/pocketbase/pocketbase/tools/rest" ) var imageContentTypes = []string{"image/png", "image/jpg", "image/jpeg"} var defaultThumbSizes = []string{"100x100"} // BindFileApi registers the file api endpoints and the corresponding handlers. func BindFileApi(app core.App, rg *echo.Group) { api := fileApi{app: app} subGroup := rg.Group("/files", ActivityLogger(app)) subGroup.GET("/:collection/:recordId/:filename", api.download, LoadCollectionContext(api.app)) } type fileApi struct { app core.App } func (api *fileApi) download(c echo.Context) error { collection, _ := c.Get(ContextCollectionKey).(*models.Collection) if collection == nil { return rest.NewNotFoundError("", nil) } recordId := c.PathParam("recordId") if recordId == "" { return rest.NewNotFoundError("", nil) } record, err := api.app.Dao().FindRecordById(collection, recordId, nil) if err != nil { return rest.NewNotFoundError("", err) } filename := c.PathParam("filename") fileField := record.FindFileFieldByFile(filename) if fileField == nil { return rest.NewNotFoundError("", nil) } options, _ := fileField.Options.(*schema.FileOptions) fs, err := api.app.NewFilesystem() if err != nil { return rest.NewBadRequestError("Filesystem initialization failure.", err) } defer fs.Close() originalPath := record.BaseFilesPath() + "/" + filename servedPath := originalPath servedName := filename // check for valid thumb size param thumbSize := c.QueryParam("thumb") if thumbSize != "" && (list.ExistInSlice(thumbSize, defaultThumbSizes) || list.ExistInSlice(thumbSize, options.Thumbs)) { // extract the original file meta attributes and check it existence oAttrs, oAttrsErr := fs.Attributes(originalPath) if oAttrsErr != nil { return rest.NewNotFoundError("", err) } // check if it is an image if list.ExistInSlice(oAttrs.ContentType, imageContentTypes) { // add thumb size as file suffix servedName = thumbSize + "_" + filename servedPath = record.BaseFilesPath() + "/thumbs_" + filename + "/" + servedName // check if the thumb exists: // - if doesn't exist - create a new thumb with the specified thumb size // - if exists - compare last modified dates to determine whether the thumb should be recreated tAttrs, tAttrsErr := fs.Attributes(servedPath) if tAttrsErr != nil || oAttrs.ModTime.After(tAttrs.ModTime) { if err := fs.CreateThumb(originalPath, servedPath, thumbSize); err != nil { servedPath = originalPath // fallback to the original } } } } event := &core.FileDownloadEvent{ HttpContext: c, Record: record, Collection: collection, FileField: fileField, ServedPath: servedPath, ServedName: servedName, } return api.app.OnFileDownloadRequest().Trigger(event, func(e *core.FileDownloadEvent) error { if err := fs.Serve(e.HttpContext.Response(), e.ServedPath, e.ServedName); err != nil { return rest.NewNotFoundError("", err) } return nil }) }