diff --git a/packages/payload/package.json b/packages/payload/package.json index f66077bea..c20656871 100644 --- a/packages/payload/package.json +++ b/packages/payload/package.json @@ -52,6 +52,7 @@ "find-up": "4.1.0", "get-tsconfig": "^4.7.2", "http-status": "1.6.2", + "image-size": "^1.1.1", "joi": "^17.12.1", "json-schema-to-typescript": "11.0.3", "jsonwebtoken": "9.0.1", @@ -62,10 +63,10 @@ "pino": "8.15.0", "pino-pretty": "10.2.0", "pluralize": "8.0.0", - "probe-image-size": "^7.2.3", "sanitize-filename": "1.6.3", "scheduler": "0.23.0", - "scmp": "2.1.0" + "scmp": "2.1.0", + "uuid": "^9.0.1" }, "peerDependencies": { "@swc/core": "^1.4.13" diff --git a/packages/payload/src/uploads/generateFileData.ts b/packages/payload/src/uploads/generateFileData.ts index cfd52d9c1..4879061e0 100644 --- a/packages/payload/src/uploads/generateFileData.ts +++ b/packages/payload/src/uploads/generateFileData.ts @@ -133,7 +133,7 @@ export const generateFileData = async ({ } if (fileSupportsResize || isImage(file.mimetype)) { - dimensions = getImageSize(file) + dimensions = await getImageSize(file) fileData.width = dimensions.width fileData.height = dimensions.height } diff --git a/packages/payload/src/uploads/getImageSize.ts b/packages/payload/src/uploads/getImageSize.ts index 82450e303..2313f5468 100644 --- a/packages/payload/src/uploads/getImageSize.ts +++ b/packages/payload/src/uploads/getImageSize.ts @@ -1,14 +1,32 @@ import fs from 'fs' -import probeImageSize from 'probe-image-size' +import sizeOfImport from 'image-size' +import { promisify } from 'util' import type { PayloadRequest } from '../types/index.js' import type { ProbedImageSize } from './types.js' -export function getImageSize(file: PayloadRequest['file']): ProbedImageSize { +import { temporaryFileTask } from './tempFile.js' + +const { imageSize } = sizeOfImport +const imageSizePromise = promisify(imageSize) + +export async function getImageSize(file: PayloadRequest['file']): Promise { if (file.tempFilePath) { - const data = fs.readFileSync(file.tempFilePath) - return probeImageSize.sync(data) + return imageSizePromise(file.tempFilePath) } - return probeImageSize.sync(file.data) + // Tiff file do not support buffers or streams, so we must write to file first + // then retrieve dimensions. https://github.com/image-size/image-size/issues/103 + if (file.mimetype === 'image/tiff') { + const dimensions = await temporaryFileTask( + async (filepath: string) => { + fs.writeFileSync(filepath, file.data) + return imageSizePromise(filepath) + }, + { extension: 'tiff' }, + ) + return dimensions + } + + return imageSize(file.data) } diff --git a/packages/payload/src/uploads/tempFile.ts b/packages/payload/src/uploads/tempFile.ts new file mode 100644 index 000000000..dd393fd31 --- /dev/null +++ b/packages/payload/src/uploads/tempFile.ts @@ -0,0 +1,50 @@ +import { promises as fsPromises } from 'fs' +import os from 'node:os' +import path from 'node:path' +import { v4 as uuid } from 'uuid' + +async function runTask(temporaryPath: string, callback) { + try { + return await callback(temporaryPath) + } finally { + await fsPromises.rm(temporaryPath, { force: true, maxRetries: 2, recursive: true }) + } +} + +type Options = { + extension?: string + name?: string +} + +export const temporaryFileTask = async (callback, options: Options = {}) => { + const filePath = await temporaryFile(options) + return runTask(filePath, callback) +} + +async function temporaryFile(options: Options) { + if (options.name) { + if (options.extension !== undefined && options.extension !== null) { + throw new Error('The `name` and `extension` options are mutually exclusive') + } + + return path.join(await temporaryDirectory(), options.name) + } + + return ( + (await getPath()) + + (options.extension === undefined || options.extension === null + ? '' + : '.' + options.extension.replace(/^\./, '')) + ) +} + +async function temporaryDirectory({ prefix = '' } = {}) { + const directory = await getPath(prefix) + await fsPromises.mkdir(directory) + return directory +} + +async function getPath(prefix = ''): Promise { + const temporaryDirectory = await fsPromises.realpath(os.tmpdir()) + return path.join(temporaryDirectory, prefix + uuid()) +} diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index c68536d18..b95b955f5 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -748,6 +748,9 @@ importers: http-status: specifier: 1.6.2 version: 1.6.2 + image-size: + specifier: ^1.1.1 + version: 1.1.1 joi: specifier: ^17.12.1 version: 17.12.3 @@ -778,9 +781,6 @@ importers: pluralize: specifier: 8.0.0 version: 8.0.0 - probe-image-size: - specifier: ^7.2.3 - version: 7.2.3 sanitize-filename: specifier: 1.6.3 version: 1.6.3 @@ -790,6 +790,9 @@ importers: scmp: specifier: 2.1.0 version: 2.1.0 + uuid: + specifier: ^9.0.1 + version: 9.0.1 devDependencies: '@monaco-editor/react': specifier: 4.5.1 @@ -8253,17 +8256,6 @@ packages: dependencies: ms: 2.0.0 - /debug@3.2.7: - resolution: {integrity: sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ==} - peerDependencies: - supports-color: '*' - peerDependenciesMeta: - supports-color: - optional: true - dependencies: - ms: 2.1.3 - dev: false - /debug@4.3.4(supports-color@5.5.0): resolution: {integrity: sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==} engines: {node: '>=6.0'} @@ -10449,6 +10441,14 @@ packages: resolution: {integrity: sha512-5Fytz/IraMjqpwfd34ke28PTVMjZjJG2MPn5t7OE4eUCUNf8BAa7b5WUS9/Qvr6mwOQS7Mk6vdsMno5he+T8Xw==} engines: {node: '>= 4'} + /image-size@1.1.1: + resolution: {integrity: sha512-541xKlUw6jr/6gGuk92F+mYM5zaFAc5ahphvkqvNe2bQ6gVBkd6bfrmVJ2t4KDAfikAYZyIqTnktX3i6/aQDrQ==} + engines: {node: '>=16.x'} + hasBin: true + dependencies: + queue: 6.0.2 + dev: false + /immer@9.0.21: resolution: {integrity: sha512-bc4NBHqOqSfRW7POMkHd51LvClaeMXpm8dx0e8oE2GORbq5aRK7Bxl4FyzVLdGtLmvLKL7BTDBG5ACQm4HWjTA==} @@ -12238,18 +12238,6 @@ packages: /natural-compare@1.4.0: resolution: {integrity: sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==} - /needle@2.9.1: - resolution: {integrity: sha512-6R9fqJ5Zcmf+uYaFgdIHmLwNldn5HbK8L5ybn7Uz+ylX/rnOsSp1AHcvQSrCaFN+qNM1wpymHqD7mVasEOlHGQ==} - engines: {node: '>= 4.4.x'} - hasBin: true - dependencies: - debug: 3.2.7 - iconv-lite: 0.4.24 - sax: 1.3.0 - transitivePeerDependencies: - - supports-color - dev: false - /negotiator@0.6.3: resolution: {integrity: sha512-+EUsqGPLsM+j/zdChZjsnX51g4XrHFOIXwfnCVPGlQk/k5giakcKsuxCObBRu6DSm9opw/O6slWbJdghQM4bBg==} engines: {node: '>= 0.6'} @@ -13815,16 +13803,6 @@ packages: engines: {node: '>=6'} dev: false - /probe-image-size@7.2.3: - resolution: {integrity: sha512-HubhG4Rb2UH8YtV4ba0Vp5bQ7L78RTONYu/ujmCu5nBI8wGv24s4E9xSKBi0N1MowRpxk76pFCpJtW0KPzOK0w==} - dependencies: - lodash.merge: 4.6.2 - needle: 2.9.1 - stream-parser: 0.3.1 - transitivePeerDependencies: - - supports-color - dev: false - /process-nextick-args@2.0.1: resolution: {integrity: sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag==} dev: true @@ -13921,6 +13899,12 @@ packages: resolution: {integrity: sha512-kJt5qhMxoszgU/62PLP1CJytzd2NKetjSRnyuj31fDd3Rlcz3fzlFdFLD1SItunPwyqEOkca6GbV612BWfaBag==} dev: true + /queue@6.0.2: + resolution: {integrity: sha512-iHZWu+q3IdFZFX36ro/lKBkSvfkztY5Y7HMiPlOUjhupPcG2JMfst2KKEpu5XndviX/3UhFbRngUPNKtgvtZiA==} + dependencies: + inherits: 2.0.4 + dev: false + /quick-format-unescaped@4.0.4: resolution: {integrity: sha512-tYC1Q1hgyRuHgloV/YXs2w15unPVh8qfu/qCTfhTYamaw7fyhumKa2yGpdSo87vY32rIclj+4fWYQXUMs9EHvg==} @@ -14499,6 +14483,7 @@ packages: /sax@1.3.0: resolution: {integrity: sha512-0s+oAmw9zLl1V1cS9BtZN7JAd0cW5e0QH4W3LWEK6a4LaLEA2OTpGYWDY+6XasBLtz6wkm3u1xRw95mRuJ59WA==} + dev: true /saxes@6.0.0: resolution: {integrity: sha512-xAg7SOnEhrm5zI3puOOKyy1OMcMlIJZYNJY7xLBwSze0UjhPLnWfj2GF2EpT0jmzaJKIWKHLsaSSajf35bcYnA==} @@ -14958,14 +14943,6 @@ packages: stubs: 3.0.0 dev: true - /stream-parser@0.3.1: - resolution: {integrity: sha512-bJ/HgKq41nlKvlhccD5kaCr/P+Hu0wPNKPJOH7en+YrJu/9EgqUF+88w5Jb6KNcjOFMhfX4B2asfeAtIGuHObQ==} - dependencies: - debug: 2.6.9 - transitivePeerDependencies: - - supports-color - dev: false - /stream-shift@1.0.3: resolution: {integrity: sha512-76ORR0DO1o1hlKwTbi/DM3EXWGf3ZJYO8cXX5RJwnul2DEg2oyoZyjLNoQM8WsvZiFKCRfC1O0J7iCvie3RZmQ==} dev: true diff --git a/tsconfig.json b/tsconfig.json index f09927d18..d55001935 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -161,4 +161,4 @@ ".next/types/**/*.ts", "scripts/**/*.ts" ] -} \ No newline at end of file +}