129 lines
3.4 KiB
TypeScript
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);
|
|
}
|