165 lines
5.5 KiB
JavaScript
165 lines
5.5 KiB
JavaScript
"use strict";
|
||
/**
|
||
*
|
||
* 参考: https://segmentfault.com/a/1190000015467084
|
||
* 优化:通过 X-Forwarded-For 添加了动态随机伪IP,绕过 tinypng 的上传数量限制
|
||
*
|
||
*/
|
||
var __importDefault = (this && this.__importDefault) || function (mod) {
|
||
return (mod && mod.__esModule) ? mod : { "default": mod };
|
||
};
|
||
Object.defineProperty(exports, "__esModule", { value: true });
|
||
const fs_1 = __importDefault(require("fs"));
|
||
const https_1 = __importDefault(require("https"));
|
||
const path_1 = __importDefault(require("path"));
|
||
const url_1 = require("url");
|
||
const exts = ['.png', '.jpg', '.jpeg'];
|
||
const max = 5200000; // 5MB == 5242848.754299136
|
||
const options = {
|
||
method: 'POST',
|
||
hostname: 'tinypng.com',
|
||
path: '/backend/opt/shrink',
|
||
headers: {
|
||
'rejectUnauthorized': 'false',
|
||
'Postman-Token': Date.now(),
|
||
'Cache-Control': 'no-cache',
|
||
'Content-Type': 'application/x-www-form-urlencoded',
|
||
'User-Agent': 'Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/56.0.2924.87 Safari/537.36'
|
||
}
|
||
};
|
||
// 生成随机IP, 赋值给 X-Forwarded-For
|
||
function getRandomIP() {
|
||
return Array.from(Array(4)).map(() => Math.floor(Math.random() * 255)).join('.');
|
||
}
|
||
// 遍历文件列表
|
||
function fileEach(folder, callback) {
|
||
fs_1.default.readdir(folder, (err, files) => {
|
||
if (err)
|
||
console.error(err);
|
||
files.forEach(file => {
|
||
const filePath = path_1.default.join(folder, file);
|
||
fs_1.default.stat(filePath, (err, stats) => {
|
||
if (err)
|
||
return console.error(err);
|
||
if (stats.isDirectory()) {
|
||
fileEach(filePath, callback);
|
||
}
|
||
else if (
|
||
// 必须是文件,小于5MB,后缀 jpg||png
|
||
stats.size <= max &&
|
||
stats.isFile() &&
|
||
exts.includes(path_1.default.extname(file))) {
|
||
callback(filePath);
|
||
}
|
||
});
|
||
});
|
||
});
|
||
}
|
||
// 压缩图片
|
||
async function fileUpload(img_path) {
|
||
return new Promise((resolve, reject) => {
|
||
// 通过 X-Forwarded-For 头部伪造客户端IP
|
||
options.headers['X-Forwarded-For'] = getRandomIP();
|
||
const req = https_1.default.request(options, function (res) {
|
||
res.on('data', buf => {
|
||
const data = JSON.parse(buf.toString());
|
||
if (data.error) {
|
||
reject(data.message);
|
||
}
|
||
else {
|
||
resolve(data);
|
||
}
|
||
});
|
||
});
|
||
req.write(fs_1.default.readFileSync(img_path), 'binary');
|
||
req.on('error', err => {
|
||
reject(err);
|
||
});
|
||
req.end();
|
||
});
|
||
}
|
||
// 该方法被循环调用,请求图片数据
|
||
function fileUpdate(img_path, obj) {
|
||
return new Promise((resolve, reject) => {
|
||
const options = new url_1.URL(obj.output.url);
|
||
const req = https_1.default.request(options, res => {
|
||
let body = '';
|
||
res.setEncoding('binary');
|
||
res.on('data', function (data) {
|
||
body += data;
|
||
});
|
||
res.on('end', function () {
|
||
fs_1.default.writeFile(img_path, body, 'binary', err => {
|
||
if (err) {
|
||
return reject(err);
|
||
}
|
||
resolve(obj);
|
||
});
|
||
});
|
||
});
|
||
req.on('error', err => {
|
||
reject(err);
|
||
});
|
||
req.end();
|
||
});
|
||
}
|
||
// 根据字节大小转成B、KB、MB
|
||
function toSize(b) {
|
||
if (b < 1024) {
|
||
return b + 'B';
|
||
}
|
||
else if (b < 1024 * 1024) {
|
||
return (b / 1024).toFixed(2) + 'KB';
|
||
}
|
||
else {
|
||
return (b / 1024 / 1024).toFixed(2) + 'MB';
|
||
}
|
||
}
|
||
// 根据小数转成百分比字符串
|
||
function toPercent(num) {
|
||
return (num * 100).toFixed(2) + '%';
|
||
}
|
||
async function fileTiny(filePath) {
|
||
return fileUpload(filePath)
|
||
.then(obj => fileUpdate(filePath, obj));
|
||
}
|
||
function default_1(folder) {
|
||
// 路径是否存在
|
||
if (!fs_1.default.existsSync(folder)) {
|
||
console.log(`路径不存在:${folder}`);
|
||
return;
|
||
}
|
||
const basename = path_1.default.basename(folder);
|
||
console.log(`[${basename}] 压缩中...`);
|
||
// 是文件
|
||
if (!fs_1.default.statSync(folder).isDirectory()) {
|
||
if (!exts.includes(path_1.default.extname(folder))) {
|
||
console.log(`[${basename}] 压缩失败!报错:只支持png、jpg与jpeg格式`);
|
||
return;
|
||
}
|
||
fileTiny(folder)
|
||
.then(obj => {
|
||
console.log('[1/1]', `[${basename}]`, `压缩成功,原始: ${toSize(obj.input.size)},压缩: ${toSize(obj.output.size)},压缩比: ${toPercent(obj.output.ratio)}`);
|
||
})
|
||
.catch(err => {
|
||
console.log('[1/1]', `[${basename}]`, `压缩失败!报错:${err}`);
|
||
});
|
||
return;
|
||
}
|
||
let total = 0;
|
||
let finished = 0;
|
||
// 是文件夹
|
||
fileEach(folder, (filePath => {
|
||
total++;
|
||
const relativePath = path_1.default.relative(folder, filePath);
|
||
fileTiny(filePath)
|
||
.then(obj => {
|
||
console.log(`[${++finished}/${total}]`, `[${relativePath}]`, `压缩成功,原始: ${toSize(obj.input.size)},压缩: ${toSize(obj.output.size)},压缩比: ${toPercent(obj.output.ratio)}`);
|
||
})
|
||
.catch(err => {
|
||
console.log(`[${++finished}/${total}]`, `[${relativePath}]`, `压缩失败!报错:${err}`);
|
||
});
|
||
}));
|
||
}
|
||
exports.default = default_1;
|