Skip to content

Commit 92d459e

Browse files
authored
fix: avoid re-uploading the file unless changed (#13500)
Fixes #13182 Before, the condition in https://github.com/payloadcms/payload/blob/b714e6b151b50b201d4470a1ebf66870e12b09ae/packages/payload/src/uploads/generateFileData.ts# was always passing. Now, with `shouldReupload` we properly check the difference (whether the image was cropped or the focal point was changed)
1 parent 7699d02 commit 92d459e

File tree

4 files changed

+58
-2
lines changed

4 files changed

+58
-2
lines changed

packages/payload/src/uploads/generateFileData.ts

Lines changed: 31 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,33 @@ type Result<T> = Promise<{
3737
files: FileToSave[]
3838
}>
3939

40+
const shouldReupload = (
41+
uploadEdits: UploadEdits,
42+
fileData: Record<string, unknown> | undefined,
43+
) => {
44+
if (!fileData) {
45+
return false
46+
}
47+
48+
if (uploadEdits.crop || uploadEdits.heightInPixels || uploadEdits.widthInPixels) {
49+
return true
50+
}
51+
52+
// Since uploadEdits always has focalPoint, compare to the value in the data if it was changed
53+
if (uploadEdits.focalPoint) {
54+
const incomingFocalX = uploadEdits.focalPoint.x
55+
const incomingFocalY = uploadEdits.focalPoint.y
56+
57+
const currentFocalX = 'focalX' in fileData && fileData.focalX
58+
const currentFocalY = 'focalY' in fileData && fileData.focalY
59+
60+
const isEqual = incomingFocalX === currentFocalX && incomingFocalY === currentFocalY
61+
return !isEqual
62+
}
63+
64+
return false
65+
}
66+
4067
export const generateFileData = async <T>({
4168
collection: { config: collectionConfig },
4269
data,
@@ -82,7 +109,10 @@ export const generateFileData = async <T>({
82109

83110
const incomingFileData = isDuplicating ? originalDoc : data
84111

85-
if (!file && uploadEdits && incomingFileData) {
112+
if (
113+
!file &&
114+
(isDuplicating || shouldReupload(uploadEdits, incomingFileData as Record<string, unknown>))
115+
) {
86116
const { filename, url } = incomingFileData as unknown as FileData
87117

88118
if (filename && (filename.includes('../') || filename.includes('..\\'))) {

test/uploads/config.ts

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -339,7 +339,12 @@ export default buildConfigWithDefaults({
339339
},
340340
{
341341
slug: mediaSlug,
342-
fields: [],
342+
fields: [
343+
{
344+
type: 'text',
345+
name: 'alt',
346+
},
347+
],
343348
upload: {
344349
staticDir: path.resolve(dirname, './media'),
345350
// crop: false,

test/uploads/e2e.spec.ts

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
import type { Page } from '@playwright/test'
22

33
import { expect, test } from '@playwright/test'
4+
import { statSync } from 'fs'
45
import { toggleColumn } from 'helpers/e2e/toggleColumn.js'
56
import { openDocDrawer } from 'helpers/e2e/toggleDocDrawer.js'
67
import path from 'path'
@@ -1603,6 +1604,24 @@ describe('Uploads', () => {
16031604
await saveDocAndAssert(page, '#action-save', 'error')
16041605
})
16051606

1607+
test('should not rewrite file when updating collection fields', async () => {
1608+
await page.goto(mediaURL.create)
1609+
await page.setInputFiles('input[type="file"]', path.resolve(dirname, './test-image.png'))
1610+
await saveDocAndAssert(page)
1611+
const imageID = page.url().split('/').pop()!
1612+
const { doc } = await client.findByID({ slug: mediaSlug, id: imageID, auth: true })
1613+
const filename = doc.filename as string
1614+
const filePath = path.resolve(dirname, 'media', filename)
1615+
const before = statSync(filePath)
1616+
1617+
const altField = page.locator('#field-alt')
1618+
await altField.fill('test alt')
1619+
1620+
await saveDocAndAssert(page)
1621+
const after = statSync(filePath)
1622+
expect(after.mtime.getTime()).toEqual(before.mtime.getTime())
1623+
})
1624+
16061625
test('should be able to replace the file even if the user doesnt have delete access', async () => {
16071626
const docID = (await payload.find({ collection: mediaWithoutDeleteAccessSlug, limit: 1 }))
16081627
.docs[0]?.id as string

test/uploads/payload-types.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -237,6 +237,7 @@ export interface Relation {
237237
*/
238238
export interface Media {
239239
id: string;
240+
alt?: string | null;
240241
updatedAt: string;
241242
createdAt: string;
242243
url?: string | null;
@@ -2395,6 +2396,7 @@ export interface FocalNoSizesSelect<T extends boolean = true> {
23952396
* via the `definition` "media_select".
23962397
*/
23972398
export interface MediaSelect<T extends boolean = true> {
2399+
alt?: T;
23982400
updatedAt?: T;
23992401
createdAt?: T;
24002402
url?: T;

0 commit comments

Comments
 (0)