From 7926083732fbaec78d87f67742cdbd8bd00cd48a Mon Sep 17 00:00:00 2001 From: James Date: Thu, 6 Oct 2022 14:23:08 -0400 Subject: [PATCH] fix: #940 --- src/collections/operations/create.ts | 17 +++++-- src/collections/operations/update.ts | 17 +++++-- .../{uploadFile.ts => generateFileData.ts} | 32 ++++++++----- src/uploads/imageResizer.ts | 48 +++++++++++-------- src/uploads/types.ts | 5 ++ src/uploads/uploadFiles.ts | 15 ++++++ 6 files changed, 98 insertions(+), 36 deletions(-) rename src/uploads/{uploadFile.ts => generateFileData.ts} (86%) create mode 100644 src/uploads/uploadFiles.ts diff --git a/src/collections/operations/create.ts b/src/collections/operations/create.ts index dc26c75caf..60476a045d 100644 --- a/src/collections/operations/create.ts +++ b/src/collections/operations/create.ts @@ -10,11 +10,12 @@ import { AfterChangeHook, BeforeOperationHook, BeforeValidateHook, Collection } import { PayloadRequest } from '../../express/types'; import { Document } from '../../types'; import { fieldAffectsData } from '../../fields/config/types'; -import uploadFile from '../../uploads/uploadFile'; +import { uploadFiles } from '../../uploads/uploadFiles'; import { beforeChange } from '../../fields/hooks/beforeChange'; import { beforeValidate } from '../../fields/hooks/beforeValidate'; import { afterChange } from '../../fields/hooks/afterChange'; import { afterRead } from '../../fields/hooks/afterRead'; +import { generateFileData } from '../../uploads/generateFileData'; export type Arguments = { collection: Collection @@ -91,10 +92,10 @@ async function create(incomingArgs: Arguments): Promise { } // ///////////////////////////////////// - // Upload and resize potential files + // Generate data for all files and sizes // ///////////////////////////////////// - data = await uploadFile({ + const { data: newFileData, files: filesToUpload } = await generateFileData({ config, collection, req, @@ -103,6 +104,8 @@ async function create(incomingArgs: Arguments): Promise { overwriteExistingFiles, }); + data = newFileData; + // ///////////////////////////////////// // beforeValidate - Fields // ///////////////////////////////////// @@ -130,6 +133,14 @@ async function create(incomingArgs: Arguments): Promise { })) || data; }, Promise.resolve()); + // ///////////////////////////////////// + // Write files to local storage + // ///////////////////////////////////// + + if (!collectionConfig.upload.disableLocalStorage) { + await uploadFiles(payload, filesToUpload); + } + // ///////////////////////////////////// // beforeChange - Collection // ///////////////////////////////////// diff --git a/src/collections/operations/update.ts b/src/collections/operations/update.ts index 723ecd5751..731c9b1f2b 100644 --- a/src/collections/operations/update.ts +++ b/src/collections/operations/update.ts @@ -8,13 +8,14 @@ import { PayloadRequest } from '../../express/types'; import { hasWhereAccessResult, UserDocument } from '../../auth/types'; import { saveCollectionDraft } from '../../versions/drafts/saveCollectionDraft'; import { saveCollectionVersion } from '../../versions/saveCollectionVersion'; -import uploadFile from '../../uploads/uploadFile'; +import { uploadFiles } from '../../uploads/uploadFiles'; import cleanUpFailedVersion from '../../versions/cleanUpFailedVersion'; import { ensurePublishedCollectionVersion } from '../../versions/ensurePublishedCollectionVersion'; import { beforeChange } from '../../fields/hooks/beforeChange'; import { beforeValidate } from '../../fields/hooks/beforeValidate'; import { afterChange } from '../../fields/hooks/afterChange'; import { afterRead } from '../../fields/hooks/afterRead'; +import { generateFileData } from '../../uploads/generateFileData'; export type Arguments = { collection: Collection @@ -125,10 +126,10 @@ async function update(incomingArgs: Arguments): Promise { }); // ///////////////////////////////////// - // Upload and resize potential files + // Generate data for all files and sizes // ///////////////////////////////////// - data = await uploadFile({ + const { data: newFileData, files: filesToUpload } = await generateFileData({ config, collection, req, @@ -137,6 +138,8 @@ async function update(incomingArgs: Arguments): Promise { overwriteExistingFiles, }); + data = newFileData; + // ///////////////////////////////////// // beforeValidate - Fields // ///////////////////////////////////// @@ -166,6 +169,14 @@ async function update(incomingArgs: Arguments): Promise { })) || data; }, Promise.resolve()); + // ///////////////////////////////////// + // Write files to local storage + // ///////////////////////////////////// + + if (!collectionConfig.upload.disableLocalStorage) { + await uploadFiles(payload, filesToUpload); + } + // ///////////////////////////////////// // beforeChange - Collection // ///////////////////////////////////// diff --git a/src/uploads/uploadFile.ts b/src/uploads/generateFileData.ts similarity index 86% rename from src/uploads/uploadFile.ts rename to src/uploads/generateFileData.ts index db35a958a5..8885458d65 100644 --- a/src/uploads/uploadFile.ts +++ b/src/uploads/generateFileData.ts @@ -10,8 +10,7 @@ import { PayloadRequest } from '../express/types'; import getImageSize, { ProbedImageSize } from './getImageSize'; import getSafeFileName from './getSafeFilename'; import resizeAndSave from './imageResizer'; -import saveBufferToFile from './saveBufferToFile'; -import { FileData } from './types'; +import { FileData, FileToSave } from './types'; import canResizeImage from './canResizeImage'; type Args = { @@ -23,7 +22,12 @@ type Args = { overwriteExistingFiles?: boolean } -const uploadFile = async ({ +type Result = Promise<{ + data: Record + files: FileToSave[] +}> + +export const generateFileData = async ({ config, collection: { config: collectionConfig, @@ -33,8 +37,9 @@ const uploadFile = async ({ data, throwOnMissingFile, overwriteExistingFiles, -}: Args): Promise> => { +}: Args): Result => { let newData = data; + const filesToSave: FileToSave[] = []; if (collectionConfig.upload) { const fileData: Partial = {}; @@ -87,9 +92,10 @@ const uploadFile = async ({ fsSafeName = await getSafeFileName(Model, staticPath, fsSafeName); } - if (!disableLocalStorage) { - await saveBufferToFile(fileBuffer, `${staticPath}/${fsSafeName}`); - } + filesToSave.push({ + path: `${staticPath}/${fsSafeName}`, + buffer: fileBuffer, + }); fileData.filename = fsSafeName || (!overwriteExistingFiles ? await getSafeFileName(Model, staticPath, file.name) : file.name); fileData.filesize = fileSize || file.size; @@ -97,7 +103,7 @@ const uploadFile = async ({ if (Array.isArray(imageSizes) && shouldResize) { req.payloadUploadSizes = {}; - fileData.sizes = await resizeAndSave({ + const { sizeData, sizesToSave } = await resizeAndSave({ req, file: file.data, dimensions, @@ -106,6 +112,9 @@ const uploadFile = async ({ savedFilename: fsSafeName || file.name, mimeType: fileData.mimeType, }); + + fileData.sizes = sizeData; + filesToSave.push(...sizesToSave); } } catch (err) { console.error(err); @@ -119,7 +128,8 @@ const uploadFile = async ({ } } - return newData; + return { + data: newData, + files: filesToSave, + }; }; - -export default uploadFile; diff --git a/src/uploads/imageResizer.ts b/src/uploads/imageResizer.ts index de32629529..7e950da39e 100644 --- a/src/uploads/imageResizer.ts +++ b/src/uploads/imageResizer.ts @@ -6,7 +6,7 @@ import { SanitizedCollectionConfig } from '../collections/config/types'; import { PayloadRequest } from '../express/types'; import fileExists from './fileExists'; import { ProbedImageSize } from './getImageSize'; -import { FileSizes, ImageSize } from './types'; +import { FileSizes, FileToSave, ImageSize } from './types'; type Args = { req: PayloadRequest @@ -25,6 +25,11 @@ type OutputImage = { height: number } +type Result = Promise<{ + sizeData: FileSizes + sizesToSave: FileToSave[] +}> + function getOutputImage(sourceImage: string, size: ImageSize): OutputImage { const extension = sourceImage.split('.').pop(); const name = sanitize(sourceImage.substring(0, sourceImage.lastIndexOf('.')) || sourceImage); @@ -44,8 +49,9 @@ export default async function resizeAndSave({ staticPath, config, savedFilename, -}: Args): Promise { - const { imageSizes, disableLocalStorage } = config.upload; +}: Args): Promise { + const { imageSizes } = config.upload; + const sizesToSave: FileToSave[] = []; const sizes = imageSizes .filter((desiredSize) => needsResize(desiredSize, dimensions)) @@ -71,9 +77,10 @@ export default async function resizeAndSave({ fs.unlinkSync(imagePath); } - if (!disableLocalStorage) { - await resized.toFile(imagePath); - } + sizesToSave.push({ + path: imagePath, + buffer: bufferObject.data, + }); return { name: desiredSize.name, @@ -87,19 +94,22 @@ export default async function resizeAndSave({ const savedSizes = await Promise.all(sizes); - return savedSizes.reduce( - (results, size) => ({ - ...results, - [size.name]: { - width: size.width, - height: size.height, - filename: size.filename, - mimeType: size.mimeType, - filesize: size.filesize, - }, - }), - {}, - ); + return { + sizeData: savedSizes.reduce( + (results, size) => ({ + ...results, + [size.name]: { + width: size.width, + height: size.height, + filename: size.filename, + mimeType: size.mimeType, + filesize: size.filesize, + }, + }), + {}, + ), + sizesToSave, + }; } function createImageName( outputImage: OutputImage, diff --git a/src/uploads/types.ts b/src/uploads/types.ts index ce5c2d1bd8..a5448adc95 100644 --- a/src/uploads/types.ts +++ b/src/uploads/types.ts @@ -78,3 +78,8 @@ export type File = { name: string size: number } + +export type FileToSave = { + buffer: Buffer + path: string +} diff --git a/src/uploads/uploadFiles.ts b/src/uploads/uploadFiles.ts new file mode 100644 index 0000000000..8a63ad1a68 --- /dev/null +++ b/src/uploads/uploadFiles.ts @@ -0,0 +1,15 @@ +import { FileUploadError } from '../errors'; +import saveBufferToFile from './saveBufferToFile'; +import { FileToSave } from './types'; +import { Payload } from '..'; + +export const uploadFiles = async (payload: Payload, files: FileToSave[]): Promise => { + try { + await Promise.all(files.map(async ({ buffer, path }) => { + await saveBufferToFile(buffer, path); + })); + } catch (err) { + payload.logger.error(err); + throw new FileUploadError(); + } +};