2014-03-05 23:00:27 -05:00
|
|
|
'use strict';
|
2014-02-09 00:34:05 -05:00
|
|
|
|
2019-10-02 18:50:03 -04:00
|
|
|
const os = require('os');
|
|
|
|
|
const fs = require('fs');
|
|
|
|
|
const path = require('path');
|
|
|
|
|
const crypto = require('crypto');
|
|
|
|
|
const winston = require('winston');
|
2019-07-09 12:46:49 -04:00
|
|
|
const util = require('util');
|
|
|
|
|
const readFileAsync = util.promisify(fs.readFile);
|
2019-10-02 18:50:03 -04:00
|
|
|
const writeFileAsync = util.promisify(fs.writeFile);
|
2018-09-20 17:05:52 -04:00
|
|
|
|
2019-10-02 18:50:03 -04:00
|
|
|
const file = require('./file');
|
|
|
|
|
const plugins = require('./plugins');
|
|
|
|
|
const meta = require('./meta');
|
2014-02-09 00:34:05 -05:00
|
|
|
|
2019-10-02 18:50:03 -04:00
|
|
|
const image = module.exports;
|
2014-02-09 00:34:05 -05:00
|
|
|
|
2018-10-04 11:38:42 -04:00
|
|
|
function requireSharp() {
|
2019-10-02 18:50:03 -04:00
|
|
|
const sharp = require('sharp');
|
2018-10-04 11:38:42 -04:00
|
|
|
if (os.platform() === 'win32') {
|
|
|
|
|
// https://github.com/lovell/sharp/issues/1259
|
|
|
|
|
sharp.cache(false);
|
|
|
|
|
}
|
|
|
|
|
return sharp;
|
|
|
|
|
}
|
|
|
|
|
|
2019-09-29 19:53:03 -04:00
|
|
|
image.isFileTypeAllowed = async function (path) {
|
|
|
|
|
const plugins = require('./plugins');
|
|
|
|
|
// deprecated: remove in 1.14.0
|
|
|
|
|
if (plugins.hasListeners('filter:file.isFileTypeAllowed')) {
|
|
|
|
|
return await plugins.fireHook('filter:file.isFileTypeAllowed', path);
|
|
|
|
|
}
|
|
|
|
|
if (plugins.hasListeners('filter:image.isFileTypeAllowed')) {
|
|
|
|
|
return await plugins.fireHook('filter:image.isFileTypeAllowed', path);
|
|
|
|
|
}
|
|
|
|
|
const sharp = require('sharp');
|
|
|
|
|
await sharp(path, {
|
|
|
|
|
failOnError: true,
|
|
|
|
|
}).metadata();
|
|
|
|
|
};
|
|
|
|
|
|
2019-07-09 12:46:49 -04:00
|
|
|
image.resizeImage = async function (data) {
|
2015-06-04 12:32:39 -04:00
|
|
|
if (plugins.hasListeners('filter:image.resize')) {
|
2019-07-09 12:46:49 -04:00
|
|
|
await plugins.fireHook('filter:image.resize', {
|
2015-09-24 12:04:24 -04:00
|
|
|
path: data.path,
|
2016-04-20 13:58:25 -04:00
|
|
|
target: data.target,
|
2015-09-24 12:04:24 -04:00
|
|
|
width: data.width,
|
2017-02-17 19:31:21 -07:00
|
|
|
height: data.height,
|
2018-03-19 16:24:15 -04:00
|
|
|
quality: data.quality,
|
2015-05-30 20:04:42 +02:00
|
|
|
});
|
2015-06-04 12:32:39 -04:00
|
|
|
} else {
|
2019-07-09 12:46:49 -04:00
|
|
|
const sharp = requireSharp();
|
|
|
|
|
const buffer = await readFileAsync(data.path);
|
|
|
|
|
const sharpImage = sharp(buffer, {
|
|
|
|
|
failOnError: true,
|
2015-09-21 15:36:08 -04:00
|
|
|
});
|
2019-07-09 12:46:49 -04:00
|
|
|
const metadata = await sharpImage.metadata();
|
|
|
|
|
|
|
|
|
|
sharpImage.rotate(); // auto-orients based on exif data
|
|
|
|
|
sharpImage.resize(data.hasOwnProperty('width') ? data.width : null, data.hasOwnProperty('height') ? data.height : null);
|
|
|
|
|
|
|
|
|
|
if (data.quality && metadata.format === 'jpeg') {
|
|
|
|
|
sharpImage.jpeg({ quality: data.quality });
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
await sharpImage.toFile(data.target || data.path);
|
2015-06-04 12:32:39 -04:00
|
|
|
}
|
2014-02-09 00:34:05 -05:00
|
|
|
};
|
|
|
|
|
|
2019-10-02 18:50:03 -04:00
|
|
|
image.normalise = async function (path) {
|
2015-06-04 12:32:39 -04:00
|
|
|
if (plugins.hasListeners('filter:image.normalise')) {
|
2019-10-02 18:50:03 -04:00
|
|
|
await plugins.fireHook('filter:image.normalise', {
|
2015-06-04 12:32:39 -04:00
|
|
|
path: path,
|
|
|
|
|
});
|
|
|
|
|
} else {
|
2019-10-02 18:50:03 -04:00
|
|
|
const sharp = requireSharp();
|
|
|
|
|
await sharp(path, { failOnError: true }).png().toFile(path + '.png');
|
2014-02-09 00:34:05 -05:00
|
|
|
}
|
2019-10-02 18:50:03 -04:00
|
|
|
return path + '.png';
|
2014-02-09 00:34:05 -05:00
|
|
|
};
|
|
|
|
|
|
2019-10-02 18:50:03 -04:00
|
|
|
image.size = async function (path) {
|
|
|
|
|
let imageData;
|
2016-05-09 22:25:51 +03:00
|
|
|
if (plugins.hasListeners('filter:image.size')) {
|
2019-10-02 18:50:03 -04:00
|
|
|
imageData = await plugins.fireHook('filter:image.size', {
|
2016-05-09 22:25:51 +03:00
|
|
|
path: path,
|
|
|
|
|
});
|
|
|
|
|
} else {
|
2019-10-02 18:50:03 -04:00
|
|
|
const sharp = requireSharp();
|
|
|
|
|
imageData = await sharp(path, { failOnError: true }).metadata();
|
2016-05-09 22:25:51 +03:00
|
|
|
}
|
2019-10-02 18:50:03 -04:00
|
|
|
return imageData ? { width: imageData.width, height: imageData.height } : undefined;
|
2016-08-30 13:10:48 +03:00
|
|
|
};
|
2016-05-01 12:44:43 +03:00
|
|
|
|
2019-10-02 18:50:03 -04:00
|
|
|
image.stripEXIF = async function (path) {
|
2019-05-23 20:38:49 -04:00
|
|
|
if (!meta.config.stripEXIFData || path.endsWith('.gif')) {
|
2019-10-02 18:50:03 -04:00
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
try {
|
|
|
|
|
const buffer = await readFileAsync(path);
|
|
|
|
|
const sharp = requireSharp();
|
|
|
|
|
await sharp(buffer, { failOnError: true }).rotate().toFile(path);
|
|
|
|
|
} catch (err) {
|
|
|
|
|
winston.error(err);
|
2019-05-09 12:00:46 -04:00
|
|
|
}
|
2019-03-06 16:36:02 -05:00
|
|
|
};
|
|
|
|
|
|
2019-10-02 18:50:03 -04:00
|
|
|
image.checkDimensions = async function (path) {
|
2018-09-20 17:05:52 -04:00
|
|
|
const meta = require('./meta');
|
2019-10-02 18:50:03 -04:00
|
|
|
const result = await image.size(path);
|
2018-09-20 17:05:52 -04:00
|
|
|
|
2019-10-02 18:50:03 -04:00
|
|
|
if (result.width > meta.config.rejectImageWidth || result.height > meta.config.rejectImageHeight) {
|
|
|
|
|
throw new Error('[[error:invalid-image-dimensions]]');
|
|
|
|
|
}
|
2018-09-20 17:05:52 -04:00
|
|
|
};
|
|
|
|
|
|
2019-10-02 18:50:03 -04:00
|
|
|
image.convertImageToBase64 = async function (path) {
|
|
|
|
|
return await readFileAsync(path, 'base64');
|
2014-03-05 23:00:27 -05:00
|
|
|
};
|
2017-02-20 21:27:56 +03:00
|
|
|
|
|
|
|
|
image.mimeFromBase64 = function (imageData) {
|
|
|
|
|
return imageData.slice(5, imageData.indexOf('base64') - 1);
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
image.extensionFromBase64 = function (imageData) {
|
|
|
|
|
return file.typeToExtension(image.mimeFromBase64(imageData));
|
|
|
|
|
};
|
|
|
|
|
|
2019-10-02 18:50:03 -04:00
|
|
|
image.writeImageDataToTempFile = async function (imageData) {
|
|
|
|
|
const filename = crypto.createHash('md5').update(imageData).digest('hex');
|
2017-02-20 21:27:56 +03:00
|
|
|
|
2019-10-02 18:50:03 -04:00
|
|
|
const type = image.mimeFromBase64(imageData);
|
|
|
|
|
const extension = file.typeToExtension(type);
|
2017-02-20 21:27:56 +03:00
|
|
|
|
2019-10-02 18:50:03 -04:00
|
|
|
const filepath = path.join(os.tmpdir(), filename + extension);
|
2017-02-20 21:27:56 +03:00
|
|
|
|
2019-10-02 18:50:03 -04:00
|
|
|
const buffer = Buffer.from(imageData.slice(imageData.indexOf('base64') + 7), 'base64');
|
2017-02-20 21:27:56 +03:00
|
|
|
|
2019-10-02 18:50:03 -04:00
|
|
|
await writeFileAsync(filepath, buffer, { encoding: 'base64' });
|
|
|
|
|
return filepath;
|
2017-02-23 18:31:49 -07:00
|
|
|
};
|
2018-09-20 19:47:44 -04:00
|
|
|
|
|
|
|
|
image.sizeFromBase64 = function (imageData) {
|
|
|
|
|
return Buffer.from(imageData.slice(imageData.indexOf('base64') + 7), 'base64').length;
|
|
|
|
|
};
|
2018-12-18 19:43:28 -05:00
|
|
|
|
2019-09-29 19:53:03 -04:00
|
|
|
image.uploadImage = async function (filename, folder, imageData) {
|
2018-12-18 19:43:28 -05:00
|
|
|
if (plugins.hasListeners('filter:uploadImage')) {
|
2019-09-29 19:53:03 -04:00
|
|
|
return await plugins.fireHook('filter:uploadImage', {
|
|
|
|
|
image: imageData,
|
|
|
|
|
uid: imageData.uid,
|
2020-05-22 10:00:24 -04:00
|
|
|
folder: folder,
|
2019-09-29 19:53:03 -04:00
|
|
|
});
|
2018-12-18 19:43:28 -05:00
|
|
|
}
|
2019-09-29 19:53:03 -04:00
|
|
|
await image.isFileTypeAllowed(imageData.path);
|
|
|
|
|
const upload = await file.saveFileToLocal(filename, folder, imageData.path);
|
|
|
|
|
return {
|
|
|
|
|
url: upload.url,
|
|
|
|
|
path: upload.path,
|
|
|
|
|
name: imageData.name,
|
|
|
|
|
};
|
2018-12-18 19:43:28 -05:00
|
|
|
};
|
2019-07-09 12:46:49 -04:00
|
|
|
|
|
|
|
|
require('./promisify')(image);
|