mirror of
				https://github.com/strapi/strapi.git
				synced 2025-11-04 11:54:10 +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 isOptimizableImage(currentFile))) {
 | 
					    if (await isImage(currentFile)) {
 | 
				
			||||||
      return currentFile;
 | 
					      if (await isFaultyImage(currentFile)) {
 | 
				
			||||||
 | 
					        throw new ApplicationError('File is not a valid image');
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
 | 
					      if (!(await isOptimizableImage(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