Files
payload/src/uploads/imageResizer.ts

129 lines
3.4 KiB
TypeScript

import { UploadedFile } from 'express-fileupload';
import { fromBuffer } from 'file-type';
import fs from 'fs';
import sanitize from 'sanitize-filename';
import sharp from 'sharp';
import { SanitizedCollectionConfig } from '../collections/config/types';
import { PayloadRequest } from '../express/types';
import fileExists from './fileExists';
import { FileSizes, FileToSave, ImageSize } from './types';
type Dimensions = { width?: number, height?: number }
type Args = {
req: PayloadRequest
file: UploadedFile
dimensions: Dimensions
staticPath: string
config: SanitizedCollectionConfig
savedFilename: string
mimeType: string
}
type OutputImage = {
name: string,
extension: string,
width: number,
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);
return {
name,
extension,
width: size.width,
height: size.height,
};
}
export default async function resizeAndSave({
req,
file,
dimensions,
staticPath,
config,
savedFilename,
}: Args): Promise<Result> {
const { imageSizes } = config.upload;
const sizesToSave: FileToSave[] = [];
const sizeData = {};
const sharpBase = sharp(file.tempFilePath || file.data);
const promises = imageSizes
.map(async (desiredSize) => {
if (!needsResize(desiredSize, dimensions)) {
sizeData[desiredSize.name] = {
url: null,
width: null,
height: null,
filename: null,
filesize: null,
mimeType: null,
};
return;
}
let resized = sharpBase.clone().resize(desiredSize);
if (desiredSize.formatOptions) {
resized = resized.toFormat(desiredSize.formatOptions.format, desiredSize.formatOptions.options);
}
const bufferObject = await resized.toBuffer({
resolveWithObject: true,
});
req.payloadUploadSizes[desiredSize.name] = bufferObject.data;
const mimeType = (await fromBuffer(bufferObject.data));
const outputImage = getOutputImage(savedFilename, desiredSize);
const imageNameWithDimensions = createImageName(outputImage, bufferObject, mimeType.ext);
const imagePath = `${staticPath}/${imageNameWithDimensions}`;
const fileAlreadyExists = await fileExists(imagePath);
if (fileAlreadyExists) {
fs.unlinkSync(imagePath);
}
sizesToSave.push({
path: imagePath,
buffer: bufferObject.data,
});
sizeData[desiredSize.name] = {
width: bufferObject.info.width,
height: bufferObject.info.height,
filename: imageNameWithDimensions,
filesize: bufferObject.info.size,
mimeType: mimeType.mime,
};
});
await Promise.all(promises);
return {
sizeData,
sizesToSave,
};
}
function createImageName(
outputImage: OutputImage,
bufferObject: { data: Buffer; info: sharp.OutputInfo },
extension: string,
): string {
return `${outputImage.name}-${bufferObject.info.width}x${bufferObject.info.height}.${extension}`;
}
function needsResize(desiredSize: ImageSize, dimensions: Dimensions): boolean {
return (typeof desiredSize.width === 'number' && desiredSize.width <= dimensions.width)
|| (typeof desiredSize.height === 'number' && desiredSize.height <= dimensions.height);
}