diff --git a/src/image.js b/src/image.js index b2a69d7e3f..4e8ad3e1c4 100644 --- a/src/image.js +++ b/src/image.js @@ -40,13 +40,14 @@ image.resizeImage = async function (data) { width: data.width, height: data.height, quality: data.quality, + type: data.type, }); } else { const sharp = requireSharp(); const buffer = await fs.promises.readFile(data.path); const sharpImage = sharp(buffer, { failOnError: true, - animated: data.path.endsWith('gif'), + animated: data.type === 'image/gif', }); const metadata = await sharpImage.metadata(); diff --git a/src/user/picture.js b/src/user/picture.js index fbff7fd225..9e7eaf6d00 100644 --- a/src/user/picture.js +++ b/src/user/picture.js @@ -87,10 +87,12 @@ module.exports = function (User) { throw new Error('[[error:invalid-image-extension]]'); } - const newPath = await convertToPNG(userPhoto.path); + const normalizedPath = await convertToPNG(userPhoto.path); + const isNormalized = userPhoto.path !== normalizedPath; await image.resizeImage({ - path: newPath, + path: normalizedPath, + type: isNormalized ? 'image/png' : userPhoto.type, width: meta.config.profileImageDimension, height: meta.config.profileImageDimension, }); @@ -98,7 +100,7 @@ module.exports = function (User) { const filename = generateProfileImageFilename(data.uid, extension); const uploadedImage = await image.uploadImage(filename, `profile/uid-${data.uid}`, { uid: data.uid, - path: newPath, + path: normalizedPath, name: 'profileAvatar', }); @@ -124,17 +126,19 @@ module.exports = function (User) { } validateUpload(data, meta.config.maximumProfileImageSize, User.getAllowedImageTypes()); - - const extension = file.typeToExtension(image.mimeFromBase64(data.imageData)); + const type = image.mimeFromBase64(data.imageData); + const extension = file.typeToExtension(type); if (!extension) { throw new Error('[[error:invalid-image-extension]]'); } picture.path = await image.writeImageDataToTempFile(data.imageData); - picture.path = await convertToPNG(picture.path); - + const normalizedPath = await convertToPNG(picture.path); + const isNormalized = picture.path !== normalizedPath; + picture.path = normalizedPath; await image.resizeImage({ path: picture.path, + type: isNormalized ? 'image/png' : type, width: meta.config.profileImageDimension, height: meta.config.profileImageDimension, }); diff --git a/test/files/animated.gif b/test/files/animated.gif new file mode 100644 index 0000000000..2d1b9e13a0 Binary files /dev/null and b/test/files/animated.gif differ diff --git a/test/user.js b/test/user.js index b2ad5970a8..f35720e217 100644 --- a/test/user.js +++ b/test/user.js @@ -1077,6 +1077,26 @@ describe('User', () => { }); }); + it('should upload a gif file as profile image', async () => { + const uid = await User.create({ username: 'giflover', password: '123456' }); + const { jar, csrf_token } = await helpers.loginUser('giflover', '123456'); + const pathToGif = path.join(__dirname, '../test/files/animated.gif'); + + const { response } = await helpers.uploadFile( + `${nconf.get('url')}/api/user/giflover/uploadpicture`, + pathToGif, { }, jar, csrf_token + ); + assert.strictEqual(response.statusCode, 200); + const picture = await db.getObjectField(`user:${uid}`, 'picture'); + const uploadedPath = path.join( + nconf.get('upload_path'), `${picture.replace(nconf.get('upload_url'), '')}` + ); + const sharp = require('sharp'); + const metadata = await sharp(uploadedPath).metadata(); + assert.strictEqual(metadata.format, 'gif'); + assert.ok(metadata.pages > 1); + }); + describe('user.uploadCroppedPicture', () => { const badImage = 'data:audio/mp3;base64,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';