Skip to content

Commit 1235a18

Browse files
authored
fix: prevent resizing of original file with withoutEnlargement on update (#12291)
This PR updates `generateFileData` to skip applying `resizeOptions` after updating an image if `resizeOptions.withoutEnlargement` is `true` and the original image size is smaller than the dimensions defined in `resizeOptions`. This prevents unintended re-resizing of already resized images when updating or modifying metadata without uploading a new file. This change ensures that: - Resizing is skipped if withoutEnlargement: true - Resizing still occurs if withoutEnlargement: false or unset This resolves an issue where images were being resized again unnecessarily when updating an upload. Fixes #12280
1 parent 81d333f commit 1235a18

File tree

5 files changed

+76
-2
lines changed

5 files changed

+76
-2
lines changed

packages/payload/src/uploads/cropImage.ts

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,33 @@ export async function cropImage({
4343
sharpOptions.animated = true
4444
}
4545

46+
const { height: originalHeight, width: originalWidth } = dimensions
47+
const newWidth = Number(widthInPixels)
48+
const newHeight = Number(heightInPixels)
49+
50+
const dimensionsChanged = originalWidth !== newWidth || originalHeight !== newHeight
51+
52+
if (!dimensionsChanged) {
53+
let adjustedHeight = originalHeight
54+
55+
if (fileIsAnimatedType) {
56+
const animatedMetadata = await sharp(
57+
file.tempFilePath || file.data,
58+
sharpOptions,
59+
).metadata()
60+
adjustedHeight = animatedMetadata.pages ? animatedMetadata.height : originalHeight
61+
}
62+
63+
return {
64+
data: file.data,
65+
info: {
66+
height: adjustedHeight,
67+
size: file.size,
68+
width: originalWidth,
69+
},
70+
}
71+
}
72+
4673
const formattedCropData = {
4774
height: Number(heightInPixels),
4875
left: percentToPixel(x, dimensions.width),

packages/payload/src/uploads/generateFileData.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -241,7 +241,7 @@ export const generateFileData = async <T>({
241241
})
242242

243243
// Apply resize after cropping to ensure it conforms to resizeOptions
244-
if (resizeOptions) {
244+
if (resizeOptions && !resizeOptions.withoutEnlargement) {
245245
const resizedAfterCrop = await sharp(croppedImage)
246246
.resize({
247247
fit: resizeOptions?.fit || 'cover',

test/uploads/config.ts

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,7 @@ import {
3030
relationSlug,
3131
unstoredMediaSlug,
3232
versionSlug,
33+
withoutEnlargeSlug,
3334
} from './shared.js'
3435
const filename = fileURLToPath(import.meta.url)
3536
const dirname = path.dirname(filename)
@@ -490,6 +491,19 @@ export default buildConfigWithDefaults({
490491
staticDir: path.resolve(dirname, './media/enlarge'),
491492
},
492493
},
494+
{
495+
slug: withoutEnlargeSlug,
496+
fields: [],
497+
upload: {
498+
resizeOptions: {
499+
width: 1000,
500+
height: undefined,
501+
fit: 'inside',
502+
withoutEnlargement: true,
503+
},
504+
staticDir: path.resolve(dirname, './media/without-enlarge'),
505+
},
506+
},
493507
{
494508
slug: reduceSlug,
495509
fields: [],

test/uploads/e2e.spec.ts

Lines changed: 33 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,7 @@ import {
3838
relationSlug,
3939
withMetadataSlug,
4040
withOnlyJPEGMetadataSlug,
41+
withoutEnlargeSlug,
4142
withoutMetadataSlug,
4243
} from './shared.js'
4344
import { startMockCorsServer } from './startMockCorsServer.js'
@@ -69,6 +70,7 @@ let uploadsTwo: AdminUrlUtil
6970
let customUploadFieldURL: AdminUrlUtil
7071
let hideFileInputOnCreateURL: AdminUrlUtil
7172
let bestFitURL: AdminUrlUtil
73+
let withoutEnlargementResizeOptionsURL: AdminUrlUtil
7274
let consoleErrorsFromPage: string[] = []
7375
let collectErrorsFromPage: () => boolean
7476
let stopCollectingErrorsFromPage: () => boolean
@@ -104,6 +106,7 @@ describe('Uploads', () => {
104106
customUploadFieldURL = new AdminUrlUtil(serverURL, customUploadFieldSlug)
105107
hideFileInputOnCreateURL = new AdminUrlUtil(serverURL, hideFileInputOnCreateSlug)
106108
bestFitURL = new AdminUrlUtil(serverURL, 'best-fit')
109+
withoutEnlargementResizeOptionsURL = new AdminUrlUtil(serverURL, withoutEnlargeSlug)
107110

108111
const context = await browser.newContext()
109112
page = await context.newPage()
@@ -1257,7 +1260,7 @@ describe('Uploads', () => {
12571260
})
12581261

12591262
// without focal point update this generated size was equal to 1736
1260-
expect(redDoc.sizes.focalTest.filesize).toEqual(1598)
1263+
expect(redDoc.sizes.focalTest.filesize).toEqual(1586)
12611264
})
12621265

12631266
test('should resize image after crop if resizeOptions defined', async () => {
@@ -1355,6 +1358,35 @@ describe('Uploads', () => {
13551358
await expect(page.locator('.file-field .file-details__remove')).toBeHidden()
13561359
})
13571360

1361+
test('should skip applying resizeOptions after updating an image if resizeOptions.withoutEnlargement is true and the original image size is smaller than the dimensions defined in resizeOptions', async () => {
1362+
await page.goto(withoutEnlargementResizeOptionsURL.create)
1363+
1364+
const fileChooserPromise = page.waitForEvent('filechooser')
1365+
await page.getByText('Select a file').click()
1366+
const fileChooser = await fileChooserPromise
1367+
await wait(1000)
1368+
await fileChooser.setFiles(path.join(dirname, 'test-image.jpg'))
1369+
1370+
await page.waitForSelector('button#action-save')
1371+
await page.locator('button#action-save').click()
1372+
await expect(page.locator('.payload-toast-container')).toContainText('successfully')
1373+
await wait(1000)
1374+
1375+
await page.locator('.file-field__edit').click()
1376+
1377+
// no need to make any changes to the image if resizeOptions.withoutEnlargement is actually being respected now
1378+
await page.locator('button:has-text("Apply Changes")').click()
1379+
await page.waitForSelector('button#action-save')
1380+
await page.locator('button#action-save').click()
1381+
await expect(page.locator('.payload-toast-container')).toContainText('successfully')
1382+
await wait(1000)
1383+
1384+
const resizeOptionMedia = page.locator('.file-meta .file-meta__size-type')
1385+
1386+
// expect the image to be the original size since the original image is smaller than the dimensions defined in resizeOptions
1387+
await expect(resizeOptionMedia).toContainText('800x800')
1388+
})
1389+
13581390
describe('imageSizes best fit', () => {
13591391
test('should select adminThumbnail if one exists', async () => {
13601392
await page.goto(bestFitURL.create)

test/uploads/shared.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ export const mediaSlug = 'media'
33
export const relationSlug = 'relation'
44
export const audioSlug = 'audio'
55
export const enlargeSlug = 'enlarge'
6+
export const withoutEnlargeSlug = 'without-enlarge'
67
export const focalNoSizesSlug = 'focal-no-sizes'
78
export const focalOnlySlug = 'focal-only'
89
export const reduceSlug = 'reduce'

0 commit comments

Comments
 (0)