|
|
|
|
@ -492,13 +492,40 @@ func (s *APIV1Service) GetAttachmentBlob(attachment *store.Attachment) ([]byte,
|
|
|
|
|
}
|
|
|
|
|
return blob, nil
|
|
|
|
|
}
|
|
|
|
|
// For S3 storage, download the file from S3.
|
|
|
|
|
if attachment.StorageType == storepb.AttachmentStorageType_S3 {
|
|
|
|
|
if attachment.Payload == nil {
|
|
|
|
|
return nil, errors.New("attachment payload is missing")
|
|
|
|
|
}
|
|
|
|
|
s3Object := attachment.Payload.GetS3Object()
|
|
|
|
|
if s3Object == nil {
|
|
|
|
|
return nil, errors.New("S3 object payload is missing")
|
|
|
|
|
}
|
|
|
|
|
if s3Object.S3Config == nil {
|
|
|
|
|
return nil, errors.New("S3 config is missing")
|
|
|
|
|
}
|
|
|
|
|
if s3Object.Key == "" {
|
|
|
|
|
return nil, errors.New("S3 object key is missing")
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
s3Client, err := s3.NewClient(context.Background(), s3Object.S3Config)
|
|
|
|
|
if err != nil {
|
|
|
|
|
return nil, errors.Wrap(err, "failed to create S3 client")
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
blob, err := s3Client.GetObject(context.Background(), s3Object.Key)
|
|
|
|
|
if err != nil {
|
|
|
|
|
return nil, errors.Wrap(err, "failed to get object from S3")
|
|
|
|
|
}
|
|
|
|
|
return blob, nil
|
|
|
|
|
}
|
|
|
|
|
// For database storage, return the blob from the database.
|
|
|
|
|
return attachment.Blob, nil
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
const (
|
|
|
|
|
// thumbnailRatio is the ratio of the thumbnail image.
|
|
|
|
|
thumbnailRatio = 0.8
|
|
|
|
|
// thumbnailMaxSize is the maximum size in pixels for the largest dimension of the thumbnail image.
|
|
|
|
|
thumbnailMaxSize = 600
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
// getOrGenerateThumbnail returns the thumbnail image of the attachment.
|
|
|
|
|
@ -523,9 +550,31 @@ func (s *APIV1Service) getOrGenerateThumbnail(attachment *store.Attachment) ([]b
|
|
|
|
|
return nil, errors.Wrap(err, "failed to decode thumbnail image")
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
thumbnailWidth := int(float64(img.Bounds().Dx()) * thumbnailRatio)
|
|
|
|
|
// Resize the image to the thumbnailWidth.
|
|
|
|
|
thumbnailImage := imaging.Resize(img, thumbnailWidth, 0, imaging.Lanczos)
|
|
|
|
|
// The largest dimension is set to thumbnailMaxSize and the smaller dimension is scaled proportionally.
|
|
|
|
|
// Small images are not enlarged.
|
|
|
|
|
width := img.Bounds().Dx()
|
|
|
|
|
height := img.Bounds().Dy()
|
|
|
|
|
var thumbnailWidth, thumbnailHeight int
|
|
|
|
|
|
|
|
|
|
// Only resize if the image is larger than thumbnailMaxSize
|
|
|
|
|
if max(width, height) > thumbnailMaxSize {
|
|
|
|
|
if width > height {
|
|
|
|
|
// Landscape or square - constrain width, maintain aspect ratio for height
|
|
|
|
|
thumbnailWidth = thumbnailMaxSize
|
|
|
|
|
thumbnailHeight = 0
|
|
|
|
|
} else {
|
|
|
|
|
// Portrait - constrain height, maintain aspect ratio for width
|
|
|
|
|
thumbnailWidth = 0
|
|
|
|
|
thumbnailHeight = thumbnailMaxSize
|
|
|
|
|
}
|
|
|
|
|
} else {
|
|
|
|
|
// Keep original dimensions for small images
|
|
|
|
|
thumbnailWidth = width
|
|
|
|
|
thumbnailHeight = height
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Resize the image to the calculated dimensions.
|
|
|
|
|
thumbnailImage := imaging.Resize(img, thumbnailWidth, thumbnailHeight, imaging.Lanczos)
|
|
|
|
|
if err := imaging.Save(thumbnailImage, filePath); err != nil {
|
|
|
|
|
return nil, errors.Wrap(err, "failed to save thumbnail file")
|
|
|
|
|
}
|
|
|
|
|
|