mirror of
https://github.com/strapi/strapi.git
synced 2025-09-02 21:32:43 +00:00

If the input file has EXIF, XMP, IPTC metadata (i.e user disabled "optimize size without quality loss" a.k.a "strip metadata" option in Admin UI), the resized images generated should keep metadata too. If the user doesn't want to keep metadata, they would let the default option "Optimize size without quality loss" enabled, right? So the resized images wouldn't keep the metadata anyway.
168 lines
3.9 KiB
JavaScript
168 lines
3.9 KiB
JavaScript
'use strict';
|
|
/**
|
|
* Image manipulation functions
|
|
*/
|
|
const sharp = require('sharp');
|
|
|
|
const { bytesToKbytes } = require('../utils/file');
|
|
|
|
const getMetadatas = buffer =>
|
|
sharp(buffer)
|
|
.metadata()
|
|
.catch(() => ({})); // ignore errors
|
|
|
|
const getDimensions = buffer =>
|
|
getMetadatas(buffer)
|
|
.then(({ width = null, height = null }) => ({ width, height }))
|
|
.catch(() => ({})); // ignore errors
|
|
|
|
const THUMBNAIL_RESIZE_OPTIONS = {
|
|
width: 245,
|
|
height: 156,
|
|
fit: 'inside',
|
|
};
|
|
|
|
const resizeTo = (buffer, options) =>
|
|
sharp(buffer)
|
|
.withMetadata()
|
|
.resize(options)
|
|
.toBuffer()
|
|
.catch(() => null);
|
|
|
|
const generateThumbnail = async file => {
|
|
if (!(await canBeProccessed(file.buffer))) {
|
|
return null;
|
|
}
|
|
|
|
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 {
|
|
name: `thumbnail_${file.name}`,
|
|
hash: `thumbnail_${file.hash}`,
|
|
ext: file.ext,
|
|
mime: file.mime,
|
|
width,
|
|
height,
|
|
size: bytesToKbytes(size),
|
|
buffer: newBuff,
|
|
path: file.path ? file.path : null,
|
|
};
|
|
}
|
|
}
|
|
|
|
return null;
|
|
};
|
|
|
|
const optimize = async buffer => {
|
|
const {
|
|
sizeOptimization = false,
|
|
autoOrientation = false,
|
|
} = await strapi.plugins.upload.services.upload.getSettings();
|
|
|
|
if (!sizeOptimization || !(await canBeProccessed(buffer))) {
|
|
return { buffer };
|
|
}
|
|
|
|
const sharpInstance = autoOrientation ? sharp(buffer).rotate() : sharp(buffer);
|
|
|
|
return sharpInstance
|
|
.toBuffer({ resolveWithObject: true })
|
|
.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),
|
|
},
|
|
};
|
|
})
|
|
.catch(() => ({ buffer }));
|
|
};
|
|
|
|
const DEFAULT_BREAKPOINTS = {
|
|
large: 1000,
|
|
medium: 750,
|
|
small: 500,
|
|
};
|
|
|
|
const getBreakpoints = () => strapi.config.get('plugins.upload.breakpoints', DEFAULT_BREAKPOINTS);
|
|
|
|
const generateResponsiveFormats = async file => {
|
|
const {
|
|
responsiveDimensions = false,
|
|
} = await strapi.plugins.upload.services.upload.getSettings();
|
|
|
|
if (!responsiveDimensions) return [];
|
|
|
|
if (!(await canBeProccessed(file.buffer))) {
|
|
return [];
|
|
}
|
|
|
|
const originalDimensions = await getDimensions(file.buffer);
|
|
|
|
const breakpoints = getBreakpoints();
|
|
return Promise.all(
|
|
Object.keys(breakpoints).map(key => {
|
|
const breakpoint = breakpoints[key];
|
|
|
|
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: {
|
|
name: `${key}_${file.name}`,
|
|
hash: `${key}_${file.hash}`,
|
|
ext: file.ext,
|
|
mime: file.mime,
|
|
width,
|
|
height,
|
|
size: bytesToKbytes(size),
|
|
buffer: newBuff,
|
|
path: file.path ? file.path : null,
|
|
},
|
|
};
|
|
}
|
|
};
|
|
|
|
const breakpointSmallerThan = (breakpoint, { width, height }) => {
|
|
return breakpoint < width || breakpoint < height;
|
|
};
|
|
|
|
const formatsToProccess = ['jpeg', 'png', 'webp', 'tiff'];
|
|
const canBeProccessed = async buffer => {
|
|
const { format } = await getMetadatas(buffer);
|
|
return format && formatsToProccess.includes(format);
|
|
};
|
|
|
|
module.exports = {
|
|
getDimensions,
|
|
generateResponsiveFormats,
|
|
generateThumbnail,
|
|
bytesToKbytes,
|
|
optimize,
|
|
};
|