2020-03-05 17:48:07 +01:00
|
|
|
'use strict';
|
|
|
|
/**
|
|
|
|
* Image manipulation functions
|
|
|
|
*/
|
|
|
|
const sharp = require('sharp');
|
|
|
|
|
2021-08-19 22:27:00 +02:00
|
|
|
const { getService } = require('../utils');
|
2020-03-06 18:41:48 +01:00
|
|
|
const { bytesToKbytes } = require('../utils/file');
|
2020-03-06 17:00:25 +01:00
|
|
|
|
|
|
|
const getMetadatas = buffer =>
|
2020-03-05 17:48:07 +01:00
|
|
|
sharp(buffer)
|
|
|
|
.metadata()
|
2020-03-09 14:04:05 +01:00
|
|
|
.catch(() => ({})); // ignore errors
|
2020-03-06 17:00:25 +01:00
|
|
|
|
|
|
|
const getDimensions = buffer =>
|
|
|
|
getMetadatas(buffer)
|
2020-04-15 19:15:21 +02:00
|
|
|
.then(({ width = null, height = null }) => ({ width, height }))
|
2020-03-09 14:04:05 +01:00
|
|
|
.catch(() => ({})); // ignore errors
|
2020-03-05 17:48:07 +01:00
|
|
|
|
2020-03-06 17:00:25 +01:00
|
|
|
const THUMBNAIL_RESIZE_OPTIONS = {
|
2020-03-05 17:48:07 +01:00
|
|
|
width: 245,
|
|
|
|
height: 156,
|
|
|
|
fit: 'inside',
|
|
|
|
};
|
|
|
|
|
2020-03-06 17:00:25 +01:00
|
|
|
const resizeTo = (buffer, options) =>
|
|
|
|
sharp(buffer)
|
2021-05-11 11:42:31 +02:00
|
|
|
.withMetadata()
|
2020-03-06 17:00:25 +01:00
|
|
|
.resize(options)
|
2020-03-05 17:48:07 +01:00
|
|
|
.toBuffer()
|
2020-03-06 17:00:25 +01:00
|
|
|
.catch(() => null);
|
|
|
|
|
|
|
|
const generateThumbnail = async file => {
|
2020-04-15 14:54:33 +02:00
|
|
|
if (!(await canBeProccessed(file.buffer))) {
|
|
|
|
return null;
|
|
|
|
}
|
|
|
|
|
2020-03-06 17:00:25 +01:00
|
|
|
const { width, height } = await getDimensions(file.buffer);
|
|
|
|
|
|
|
|
if (width > THUMBNAIL_RESIZE_OPTIONS.width || height > THUMBNAIL_RESIZE_OPTIONS.height) {
|
|
|
|
const newBuff = await resizeTo(file.buffer, THUMBNAIL_RESIZE_OPTIONS);
|
|
|
|
|
|
|
|
if (newBuff) {
|
|
|
|
const { width, height, size } = await getMetadatas(newBuff);
|
|
|
|
|
|
|
|
return {
|
2020-06-25 11:13:42 +03:00
|
|
|
name: `thumbnail_${file.name}`,
|
2020-03-09 09:02:51 +01:00
|
|
|
hash: `thumbnail_${file.hash}`,
|
2020-03-05 17:48:07 +01:00
|
|
|
ext: file.ext,
|
2020-03-06 17:00:25 +01:00
|
|
|
mime: file.mime,
|
|
|
|
width,
|
|
|
|
height,
|
|
|
|
size: bytesToKbytes(size),
|
|
|
|
buffer: newBuff,
|
2020-05-07 09:28:26 +02:00
|
|
|
path: file.path ? file.path : null,
|
2020-03-06 17:00:25 +01:00
|
|
|
};
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return null;
|
2020-03-05 17:48:07 +01:00
|
|
|
};
|
|
|
|
|
2020-03-08 12:59:21 +01:00
|
|
|
const optimize = async buffer => {
|
2021-08-19 22:27:00 +02:00
|
|
|
const { sizeOptimization = false, autoOrientation = false } = await getService(
|
|
|
|
'upload'
|
|
|
|
).getSettings();
|
2020-03-08 18:43:50 +01:00
|
|
|
|
2020-06-23 16:38:04 +03:00
|
|
|
if (!sizeOptimization || !(await canBeProccessed(buffer))) {
|
2020-04-15 14:54:33 +02:00
|
|
|
return { buffer };
|
|
|
|
}
|
|
|
|
|
2020-06-23 16:38:04 +03:00
|
|
|
const sharpInstance = autoOrientation ? sharp(buffer).rotate() : sharp(buffer);
|
2021-01-25 18:21:44 +01:00
|
|
|
|
2020-06-23 16:38:04 +03:00
|
|
|
return sharpInstance
|
2020-03-08 18:43:50 +01:00
|
|
|
.toBuffer({ resolveWithObject: true })
|
2021-01-25 18:21:44 +01:00
|
|
|
.then(({ data, info }) => {
|
|
|
|
const output = buffer.length < data.length ? buffer : data;
|
|
|
|
|
|
|
|
return {
|
|
|
|
buffer: output,
|
|
|
|
info: {
|
|
|
|
width: info.width,
|
|
|
|
height: info.height,
|
|
|
|
size: bytesToKbytes(output.length),
|
|
|
|
},
|
|
|
|
};
|
|
|
|
})
|
2020-03-08 18:43:50 +01:00
|
|
|
.catch(() => ({ buffer }));
|
2020-03-08 12:59:21 +01:00
|
|
|
};
|
|
|
|
|
2021-01-28 16:31:09 +00:00
|
|
|
const DEFAULT_BREAKPOINTS = {
|
2020-03-08 20:03:45 +01:00
|
|
|
large: 1000,
|
|
|
|
medium: 750,
|
|
|
|
small: 500,
|
|
|
|
};
|
|
|
|
|
2021-08-06 18:09:49 +02:00
|
|
|
const getBreakpoints = () => strapi.config.get('plugin.upload.breakpoints', DEFAULT_BREAKPOINTS);
|
2021-01-28 16:31:09 +00:00
|
|
|
|
2020-03-08 20:03:45 +01:00
|
|
|
const generateResponsiveFormats = async file => {
|
2021-08-19 22:27:00 +02:00
|
|
|
const { responsiveDimensions = false } = await getService('upload').getSettings();
|
2020-03-08 20:03:45 +01:00
|
|
|
|
2020-03-20 18:32:29 +01:00
|
|
|
if (!responsiveDimensions) return [];
|
2020-03-08 20:03:45 +01:00
|
|
|
|
2020-04-15 14:54:33 +02:00
|
|
|
if (!(await canBeProccessed(file.buffer))) {
|
|
|
|
return [];
|
|
|
|
}
|
|
|
|
|
2020-03-08 20:03:45 +01:00
|
|
|
const originalDimensions = await getDimensions(file.buffer);
|
|
|
|
|
2021-01-28 16:31:09 +00:00
|
|
|
const breakpoints = getBreakpoints();
|
2020-03-08 20:03:45 +01:00
|
|
|
return Promise.all(
|
2021-01-28 16:31:09 +00:00
|
|
|
Object.keys(breakpoints).map(key => {
|
|
|
|
const breakpoint = breakpoints[key];
|
2020-03-08 20:03:45 +01:00
|
|
|
|
|
|
|
if (breakpointSmallerThan(breakpoint, originalDimensions)) {
|
|
|
|
return generateBreakpoint(key, { file, breakpoint, originalDimensions });
|
|
|
|
}
|
|
|
|
})
|
|
|
|
);
|
|
|
|
};
|
|
|
|
|
|
|
|
const generateBreakpoint = async (key, { file, breakpoint }) => {
|
|
|
|
const newBuff = await resizeTo(file.buffer, {
|
|
|
|
width: breakpoint,
|
|
|
|
height: breakpoint,
|
|
|
|
fit: 'inside',
|
|
|
|
});
|
|
|
|
|
|
|
|
if (newBuff) {
|
|
|
|
const { width, height, size } = await getMetadatas(newBuff);
|
|
|
|
|
|
|
|
return {
|
|
|
|
key,
|
|
|
|
file: {
|
2020-06-25 11:13:42 +03:00
|
|
|
name: `${key}_${file.name}`,
|
2020-03-08 20:03:45 +01:00
|
|
|
hash: `${key}_${file.hash}`,
|
|
|
|
ext: file.ext,
|
|
|
|
mime: file.mime,
|
|
|
|
width,
|
|
|
|
height,
|
|
|
|
size: bytesToKbytes(size),
|
|
|
|
buffer: newBuff,
|
2020-05-07 09:28:26 +02:00
|
|
|
path: file.path ? file.path : null,
|
2020-03-08 20:03:45 +01:00
|
|
|
},
|
|
|
|
};
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
|
|
|
const breakpointSmallerThan = (breakpoint, { width, height }) => {
|
|
|
|
return breakpoint < width || breakpoint < height;
|
|
|
|
};
|
|
|
|
|
2020-04-15 14:54:33 +02:00
|
|
|
const formatsToProccess = ['jpeg', 'png', 'webp', 'tiff'];
|
|
|
|
const canBeProccessed = async buffer => {
|
|
|
|
const { format } = await getMetadatas(buffer);
|
|
|
|
return format && formatsToProccess.includes(format);
|
|
|
|
};
|
|
|
|
|
2021-07-08 11:20:13 +02:00
|
|
|
module.exports = () => ({
|
2020-03-05 17:48:07 +01:00
|
|
|
getDimensions,
|
2020-03-08 20:03:45 +01:00
|
|
|
generateResponsiveFormats,
|
2020-03-05 17:48:07 +01:00
|
|
|
generateThumbnail,
|
2020-03-06 17:00:25 +01:00
|
|
|
bytesToKbytes,
|
2020-03-08 12:59:21 +01:00
|
|
|
optimize,
|
2021-07-08 11:20:13 +02:00
|
|
|
});
|