mirror of
https://github.com/pocketbase/pocketbase.git
synced 2024-11-25 09:21:11 +02:00
105 lines
3.1 KiB
Go
105 lines
3.1 KiB
Go
|
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, false); 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
|
||
|
})
|
||
|
}
|