chore(attachments): refactor building image preview
Some checks failed
continuous-integration/drone/push Build is failing

This commit is contained in:
kolaente 2024-09-06 09:43:41 +02:00
parent cbc63c853d
commit 02c1de55c4
Signed by: konrad
GPG Key ID: F40E70337AB24C9B
7 changed files with 44 additions and 39 deletions

View File

@ -180,7 +180,7 @@ import ProgressBar from '@/components/misc/ProgressBar.vue'
import BaseButton from '@/components/base/BaseButton.vue'
import AttachmentService from '@/services/attachment'
import {SUPPORTED_IMAGE_SUFFIX} from '@/models/attachment'
import {canPreview} from '@/models/attachment'
import type {IAttachment} from '@/modelTypes/IAttachment'
import type {ITask} from '@/modelTypes/ITask'
@ -274,10 +274,6 @@ async function viewOrDownload(attachment: IAttachment) {
}
}
function canPreview(attachment: IAttachment): boolean {
return SUPPORTED_IMAGE_SUFFIX.some((suffix) => attachment.file.name.toLowerCase().endsWith(suffix))
}
const copy = useCopyToClipboard()
function copyUrl(attachment: IAttachment) {

View File

@ -22,7 +22,7 @@
import {ref, shallowReactive, watchEffect} from 'vue'
import AttachmentService, {PREVIEW_SIZE} from '@/services/attachment'
import type {IAttachment} from '@/modelTypes/IAttachment'
import {SUPPORTED_IMAGE_SUFFIX} from '@/models/attachment'
import {canPreview} from '@/models/attachment'
const props = defineProps<{
modelValue?: IAttachment
@ -33,13 +33,9 @@ const blobUrl = ref<string | undefined>(undefined)
watchEffect(async () => {
if (props.modelValue && canPreview(props.modelValue)) {
blobUrl.value = await attachmentService.getBlobUrl(props.modelValue, PREVIEW_SIZE.SM)
blobUrl.value = await attachmentService.getBlobUrl(props.modelValue, PREVIEW_SIZE.MD)
}
})
function canPreview(attachment: IAttachment): boolean {
return SUPPORTED_IMAGE_SUFFIX.some((suffix) => attachment.file.name.toLowerCase().endsWith(suffix))
}
</script>
<style scoped lang="scss">

View File

@ -165,7 +165,7 @@ async function maybeDownloadCoverImage() {
}
const attachmentService = new AttachmentService()
coverImageBlobUrl.value = await attachmentService.getBlobUrl(attachment, PREVIEW_SIZE.MD)
coverImageBlobUrl.value = await attachmentService.getBlobUrl(attachment, PREVIEW_SIZE.LG)
}
watch(

View File

@ -7,6 +7,10 @@ import type { IAttachment } from '@/modelTypes/IAttachment'
export const SUPPORTED_IMAGE_SUFFIX = ['.jpg', '.png', '.bmp', '.gif']
export function canPreview(attachment: IAttachment): boolean {
return SUPPORTED_IMAGE_SUFFIX.some((suffix) => attachment.file.name.toLowerCase().endsWith(suffix))
}
export default class AttachmentModel extends AbstractModel<IAttachment> implements IAttachment {
id = 0
taskId = 0

View File

@ -47,7 +47,7 @@ export default class AttachmentService extends AbstractService<IAttachment> {
getBlobUrl(model: IAttachment, size?: PREVIEW_SIZE) {
let mainUrl = '/tasks/' + model.taskId + '/attachments/' + model.id
if (size !== undefined) {
mainUrl += `?preview=true&size=${size}`
mainUrl += `?preview_size=${size}`
}
return AbstractService.prototype.getBlobUrl.call(this, mainUrl)

View File

@ -213,10 +213,11 @@ func (ta *TaskAttachment) GetPreviewFromCache(previewSize PreviewSize) []byte {
type PreviewSize string
const (
PreviewSmall PreviewSize = "sm"
PreviewMedium PreviewSize = "md"
PreviewLarge PreviewSize = "lg"
PreviewExtraLarge PreviewSize = "xl"
PreviewSizeUnknown PreviewSize = "unknown"
PreviewSmall PreviewSize = "sm"
PreviewMedium PreviewSize = "md"
PreviewLarge PreviewSize = "lg"
PreviewExtraLarge PreviewSize = "xl"
)
func (previewSize PreviewSize) GetSize() int {
@ -229,22 +230,37 @@ func (previewSize PreviewSize) GetSize() int {
return 400
case PreviewExtraLarge:
return 800
case PreviewSizeUnknown:
return 0
default:
return 200
}
}
func resize(img image.Image, size int) *image.NRGBA {
x := img.Bounds().Size().X
y := img.Bounds().Size().Y
heightSmaller := x > y
var resizedImg *image.NRGBA
if heightSmaller {
resizedImg = imaging.Resize(img, 0, size, imaging.Lanczos)
} else {
resizedImg = imaging.Resize(img, size, 0, imaging.Lanczos)
func GetPreviewSizeFromString(size string) PreviewSize {
switch size {
case "sm":
return PreviewSmall
case "md":
return PreviewMedium
case "lg":
return PreviewLarge
case "xl":
return PreviewExtraLarge
}
log.Debugf("Resized attachment image from %vx%v to %vx%v for a preview", x, y, resizedImg.Bounds().Size().X, resizedImg.Bounds().Size().Y)
return PreviewSizeUnknown
}
func resizeImage(img image.Image, width int) *image.NRGBA {
resizedImg := imaging.Resize(img, width, 0, imaging.Lanczos)
log.Debugf(
"Resized attachment image from %vx%v to %vx%v for a preview",
img.Bounds().Size().X,
img.Bounds().Size().Y,
resizedImg.Bounds().Size().X,
resizedImg.Bounds().Size().Y,
)
return resizedImg
}
@ -256,7 +272,7 @@ func (ta *TaskAttachment) GenerateAndSavePreviewToCache(previewSize PreviewSize)
}
// Scale down the image to a minimum size
resizedImg := resize(img, previewSize.GetSize())
resizedImg := resizeImage(img, previewSize.GetSize())
// Get the raw bytes of the resized image
buf := &bytes.Buffer{}

View File

@ -117,8 +117,7 @@ func UploadTaskAttachment(c echo.Context) error {
// @Produce octet-stream
// @Param id path int true "Task ID"
// @Param attachmentID path int true "Attachment ID"
// @Param preview query string false "If set to true, a preview image will be returned if the attachment is an image."
// @Param size query string false "The size of the preview image. Can be sm = 100px, md = 200px, lg = 400px or xl = 800px."
// @Param preview_size query string false "The size of the preview image. Can be sm = 100px, md = 200px, lg = 400px or xl = 800px. If provided, a preview image will be returned if the attachment is an image."
// @Security JWTKeyAuth
// @Success 200 {file} blob "The attachment file."
// @Failure 403 {object} models.Message "No access to this task."
@ -157,15 +156,9 @@ func GetTaskAttachment(c echo.Context) error {
return handler.HandleHTTPError(err)
}
// Reading the 'preview' query parameter
preview := c.QueryParam("preview") == "true"
previewSize := models.PreviewSize(c.QueryParam("size"))
if previewSize == "" {
previewSize = models.PreviewMedium
}
// If the preview query parameter is set and the preview was already generated and cached, return the cached preview image
if preview && strings.HasPrefix(taskAttachment.File.Mime, "image") {
previewSize := models.GetPreviewSizeFromString(c.QueryParam("preview_size"))
if previewSize != models.PreviewSizeUnknown && strings.HasPrefix(taskAttachment.File.Mime, "image") {
previewFileBytes := taskAttachment.GetPreviewFromCache(previewSize)
if previewFileBytes != nil {
log.Debugf("Cached attachment image preview found for task attachment %v", taskAttachment.ID)
@ -187,7 +180,7 @@ func GetTaskAttachment(c echo.Context) error {
}
// If a preview is requested and the preview was not cached, we create the preview and cache it
if preview {
if previewSize != models.PreviewSizeUnknown {
previewFileBytes := taskAttachment.GenerateAndSavePreviewToCache(previewSize)
if previewFileBytes != nil {
return c.Blob(http.StatusOK, "image/png", previewFileBytes)