mirror of
https://github.com/strapi/strapi.git
synced 2025-11-09 14:51:29 +00:00
check if image is faulty in enhanceFile
This commit is contained in:
parent
096dba7983
commit
d61104de72
@ -4,11 +4,10 @@
|
|||||||
*/
|
*/
|
||||||
const fs = require('fs');
|
const fs = require('fs');
|
||||||
const { join } = require('path');
|
const { join } = require('path');
|
||||||
const { ApplicationError } = require('@strapi/utils').errors;
|
|
||||||
const sharp = require('sharp');
|
const sharp = require('sharp');
|
||||||
|
|
||||||
const { getService } = require('../utils');
|
const { getService } = require('../utils');
|
||||||
const { bytesToKbytes } = require('../utils/file');
|
const { bytesToKbytes, writableStreamDiscard } = require('../utils/file');
|
||||||
|
|
||||||
const FORMATS_TO_PROCESS = ['jpeg', 'png', 'webp', 'tiff', 'svg', 'gif'];
|
const FORMATS_TO_PROCESS = ['jpeg', 'png', 'webp', 'tiff', 'svg', 'gif'];
|
||||||
const FORMATS_TO_OPTIMIZE = ['jpeg', 'png', 'webp', 'tiff'];
|
const FORMATS_TO_OPTIMIZE = ['jpeg', 'png', 'webp', 'tiff'];
|
||||||
@ -47,12 +46,7 @@ const THUMBNAIL_RESIZE_OPTIONS = {
|
|||||||
const resizeFileTo = async (file, options, { name, hash }) => {
|
const resizeFileTo = async (file, options, { name, hash }) => {
|
||||||
const filePath = join(file.tmpWorkingDirectory, hash);
|
const filePath = join(file.tmpWorkingDirectory, hash);
|
||||||
|
|
||||||
try {
|
|
||||||
await writeStreamToFile(file.getStream().pipe(sharp().resize(options)), filePath);
|
await writeStreamToFile(file.getStream().pipe(sharp().resize(options)), filePath);
|
||||||
} catch (err) {
|
|
||||||
throw new ApplicationError('File is not a valid image');
|
|
||||||
}
|
|
||||||
|
|
||||||
const newFile = {
|
const newFile = {
|
||||||
name,
|
name,
|
||||||
hash,
|
hash,
|
||||||
@ -108,11 +102,7 @@ const optimize = async file => {
|
|||||||
}
|
}
|
||||||
const filePath = join(file.tmpWorkingDirectory, `optimized-${file.hash}`);
|
const filePath = join(file.tmpWorkingDirectory, `optimized-${file.hash}`);
|
||||||
|
|
||||||
try {
|
|
||||||
await writeStreamToFile(file.getStream().pipe(transformer), filePath);
|
await writeStreamToFile(file.getStream().pipe(transformer), filePath);
|
||||||
} catch {
|
|
||||||
throw new ApplicationError('File is not a valid image');
|
|
||||||
}
|
|
||||||
|
|
||||||
newFile.getStream = () => fs.createReadStream(filePath);
|
newFile.getStream = () => fs.createReadStream(filePath);
|
||||||
}
|
}
|
||||||
@ -190,6 +180,20 @@ const isSupportedImage = (...args) => {
|
|||||||
return isOptimizableImage(...args);
|
return isOptimizableImage(...args);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Applies a simple image transformation to see if the image is faulty/corrupted.
|
||||||
|
*/
|
||||||
|
const isFaultyImage = file =>
|
||||||
|
new Promise(resolve => {
|
||||||
|
file
|
||||||
|
.getStream()
|
||||||
|
.pipe(sharp().rotate())
|
||||||
|
.on('error', () => resolve(true))
|
||||||
|
.pipe(writableStreamDiscard())
|
||||||
|
.on('error', () => resolve(true))
|
||||||
|
.on('close', () => resolve(false));
|
||||||
|
});
|
||||||
|
|
||||||
const isOptimizableImage = async file => {
|
const isOptimizableImage = async file => {
|
||||||
let format;
|
let format;
|
||||||
try {
|
try {
|
||||||
@ -216,6 +220,7 @@ const isImage = async file => {
|
|||||||
|
|
||||||
module.exports = () => ({
|
module.exports = () => ({
|
||||||
isSupportedImage,
|
isSupportedImage,
|
||||||
|
isFaultyImage,
|
||||||
isOptimizableImage,
|
isOptimizableImage,
|
||||||
isImage,
|
isImage,
|
||||||
getDimensions,
|
getDimensions,
|
||||||
|
|||||||
@ -22,6 +22,7 @@ const { NotFoundError } = require('@strapi/utils').errors;
|
|||||||
|
|
||||||
const { MEDIA_UPDATE, MEDIA_CREATE, MEDIA_DELETE } = webhookUtils.webhookEvents;
|
const { MEDIA_UPDATE, MEDIA_CREATE, MEDIA_DELETE } = webhookUtils.webhookEvents;
|
||||||
|
|
||||||
|
const { ApplicationError } = require('@strapi/utils/lib/errors');
|
||||||
const { FILE_MODEL_UID } = require('../constants');
|
const { FILE_MODEL_UID } = require('../constants');
|
||||||
const { getService } = require('../utils');
|
const { getService } = require('../utils');
|
||||||
const { bytesToKbytes } = require('../utils/file');
|
const { bytesToKbytes } = require('../utils/file');
|
||||||
@ -106,7 +107,7 @@ module.exports = ({ strapi }) => ({
|
|||||||
return entity;
|
return entity;
|
||||||
},
|
},
|
||||||
|
|
||||||
async enhanceFile(file, fileInfo = {}, metas = {}) {
|
async enhanceAndValidateFile(file, fileInfo = {}, metas = {}) {
|
||||||
const currentFile = await this.formatFileInfo(
|
const currentFile = await this.formatFileInfo(
|
||||||
{
|
{
|
||||||
filename: file.name,
|
filename: file.name,
|
||||||
@ -121,14 +122,29 @@ module.exports = ({ strapi }) => ({
|
|||||||
);
|
);
|
||||||
currentFile.getStream = () => fs.createReadStream(file.path);
|
currentFile.getStream = () => fs.createReadStream(file.path);
|
||||||
|
|
||||||
const { optimize, isOptimizableImage } = strapi.plugin('upload').service('image-manipulation');
|
const { optimize, isImage, isFaultyImage, isOptimizableImage } = strapi
|
||||||
|
.plugin('upload')
|
||||||
|
.service('image-manipulation');
|
||||||
|
|
||||||
|
if (await isImage(currentFile)) {
|
||||||
|
if (await isFaultyImage(currentFile)) {
|
||||||
|
throw new ApplicationError('File is not a valid image');
|
||||||
|
}
|
||||||
if (!(await isOptimizableImage(currentFile))) {
|
if (!(await isOptimizableImage(currentFile))) {
|
||||||
return currentFile;
|
return currentFile;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
return optimize(currentFile);
|
return optimize(currentFile);
|
||||||
},
|
},
|
||||||
|
|
||||||
|
// TODO V5: remove enhanceFile
|
||||||
|
async enhanceFile(file, fileInfo = {}, metas = {}) {
|
||||||
|
process.emitWarning(
|
||||||
|
'[deprecated] In future versions, `enhanceFile` will be removed. Replace it with `enhanceAndValidateFile` instead.'
|
||||||
|
);
|
||||||
|
return this.enhanceAndValidateFile(file, fileInfo, metas);
|
||||||
|
},
|
||||||
|
|
||||||
async upload({ data, files }, { user } = {}) {
|
async upload({ data, files }, { user } = {}) {
|
||||||
// create temporary folder to store files for stream manipulation
|
// create temporary folder to store files for stream manipulation
|
||||||
const tmpWorkingDirectory = await createAndAssignTmpWorkingDirectoryToFiles(files);
|
const tmpWorkingDirectory = await createAndAssignTmpWorkingDirectoryToFiles(files);
|
||||||
@ -142,7 +158,7 @@ module.exports = ({ strapi }) => ({
|
|||||||
const fileInfoArray = Array.isArray(fileInfo) ? fileInfo : [fileInfo];
|
const fileInfoArray = Array.isArray(fileInfo) ? fileInfo : [fileInfo];
|
||||||
|
|
||||||
const doUpload = async (file, fileInfo) => {
|
const doUpload = async (file, fileInfo) => {
|
||||||
const fileData = await this.enhanceFile(file, fileInfo, metas);
|
const fileData = await this.enhanceAndValidateFile(file, fileInfo, metas);
|
||||||
return this.uploadFileAndPersist(fileData, { user });
|
return this.uploadFileAndPersist(fileData, { user });
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -266,7 +282,7 @@ module.exports = ({ strapi }) => ({
|
|||||||
|
|
||||||
try {
|
try {
|
||||||
const { fileInfo } = data;
|
const { fileInfo } = data;
|
||||||
fileData = await this.enhanceFile(file, fileInfo);
|
fileData = await this.enhanceAndValidateFile(file, fileInfo);
|
||||||
|
|
||||||
// keep a constant hash and extension so the file url doesn't change when the file is replaced
|
// keep a constant hash and extension so the file url doesn't change when the file is replaced
|
||||||
_.assign(fileData, {
|
_.assign(fileData, {
|
||||||
@ -386,7 +402,7 @@ module.exports = ({ strapi }) => ({
|
|||||||
try {
|
try {
|
||||||
const enhancedFiles = await Promise.all(
|
const enhancedFiles = await Promise.all(
|
||||||
arr.map(file => {
|
arr.map(file => {
|
||||||
return this.enhanceFile(
|
return this.enhanceAndValidateFile(
|
||||||
file,
|
file,
|
||||||
{ folder: apiUploadFolder.id },
|
{ folder: apiUploadFolder.id },
|
||||||
{
|
{
|
||||||
|
|||||||
@ -2,6 +2,7 @@
|
|||||||
/**
|
/**
|
||||||
* Utils file containing file treatment utils
|
* Utils file containing file treatment utils
|
||||||
*/
|
*/
|
||||||
|
const { Writable } = require('stream');
|
||||||
|
|
||||||
const bytesToKbytes = bytes => Math.round((bytes / 1000) * 100) / 100;
|
const bytesToKbytes = bytes => Math.round((bytes / 1000) * 100) / 100;
|
||||||
|
|
||||||
@ -26,8 +27,22 @@ const getStreamSize = stream =>
|
|||||||
stream.resume();
|
stream.resume();
|
||||||
});
|
});
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create a writeable Node.js stream that discards received data.
|
||||||
|
* Useful for testing, draining a stream of data, etc.
|
||||||
|
*/
|
||||||
|
function writableStreamDiscard(options) {
|
||||||
|
return new Writable({
|
||||||
|
...options,
|
||||||
|
write(chunk, encding, callback) {
|
||||||
|
setImmediate(callback);
|
||||||
|
},
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
module.exports = {
|
module.exports = {
|
||||||
streamToBuffer,
|
streamToBuffer,
|
||||||
bytesToKbytes,
|
bytesToKbytes,
|
||||||
getStreamSize,
|
getStreamSize,
|
||||||
|
writableStreamDiscardData: writableStreamDiscard,
|
||||||
};
|
};
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user