-前言-
博主目前還是主力使用Laya進行遊戲開發,隨着項目擴大,資源量增加,發佈愈發的緩慢。究其根本還是問題出在了圖片壓縮問題上,實在受不了準備將壓縮流程剝離出來,在自己想壓縮的時候再執行命令壓縮也不遲。
-正文-
首先來說一說爲什麼圖片壓縮那麼慢,圖片壓縮分爲兩個步驟:1.PNG壓縮,採用了pngquant,改工具在時間空間複雜度都不高。2.JPEG格式壓縮,採用了guetzli,這個工具就是讓你在壓縮時電腦卡得不行的工具,隨着壓縮的圖片越大暫用率越高。
這裏就不討論兩個工具的使用了,也不討論他們的效率,guetzli雖然效率低不過在jpg格式中的壓縮率還是很不錯,只要我們合適的時候使用也不會影響我們的心情:)
思路
其實思路還是很簡單的,讀取指定目錄下的所有png、jpg然後調用pngquant或者guetzli工具,值得注意的是兩個工具都只有單張圖片的壓縮方式,因此需要不停的調用子進程進行壓縮。png是遊戲中使用較多的圖片格式,jpg一般用於用戶頭像,遊戲log這些應用場景頻率較小的地方,更改頻率也比較小,因此我們可以將工具分爲png壓縮、jpg壓縮、圖片壓縮三個命令,視情況選擇執行。
使用工具時需要安裝下載guetzli,pngquant
1.批處理文件
首先我們定義三個批處理文件,分別處理png壓縮任務,jpg壓縮任務,圖片壓縮任務。
//bat jpg壓縮
node index.js jpg
@pause
//bat png壓縮
node index.js png
@pause
//壓縮png jpg
node index.js png jpg
@pause
2.讀取指定目錄下的所有文件並篩選PNG、JPG文件
//1.首先讀取指定目錄下的所有文件路徑
/**
* 讀取指定路徑下的所有文件路徑並賦值到out中
* @param {string} parentPath
* @param {Array<string>} out
*/
function readAll(parentPath, out) {
try {
let files = fs.readdirSync(parentPath);
files.forEach(function (item) {
let tempPath = path.join(parentPath, item);
let stats = fs.statSync(tempPath);
if (stats.isDirectory()) {
readAll(tempPath, out);
} else {
out.push(tempPath);
}
});
} catch (e) {
console.warn("Path Error:" + e);
return out;
}
}
//2.篩選png,jpg
let allFileList = [];
readAll(root_path, allFileList);
let pngList = [];
let jpgList = [];
for (let i = 0; i < allFileList.length; i++) {
let fileData = path.parse(allFileList[i]);
if (fileData.ext == ".png") {
pngList.push(allFileList[i]);
} else if (fileData.ext == ".jpg" || fileData.ext == ".JPG") {
jpgList.push(allFileList[i]);
}
}
3.根據進程參數執行壓縮
之前我們定義的bat分別向nodejs程序傳入的執行參數,這時候我們就可以通過參數來指定執行什麼函數。
if (process.argv.indexOf(TYPE_PNG) != -1) {
//1.壓縮png
compressPNG(pngList, compressCache.PNG, onCompressComplete);
} else {
isPngComplete = true;
}
if (process.argv.indexOf(TYPE_JPG) != -1) {
//2.壓縮jpg
compressJPG(jpgList, compressCache.JPG, onCompressComplete);
} else {
isJpgComplete = true;
}
//所有壓縮完成回調
function onCompressComplete(type) {
if (type == TYPE_JPG) {
isJpgComplete = true;
console.log("JPG 壓縮完成.");
}
if (type == TYPE_PNG) {
isPngComplete = true;
console.log("PNG 壓縮完成.")
}
if (isPngComplete && isJpgComplete) {
let encodeCache = JSON.stringify(compressCache);
//回寫cache
fs.writeFileSync("./cache/cache.json", encodeCache, "utf-8");
console.log("壓縮完成...");
}
}
壓縮的時候做了一個小小的優化,通過緩存圖片文件的大小及修改時間來判斷是否需要壓縮文件,對於沒有修改的文件就不進行壓縮。通過文件名轉md5存到cache.json文件中。
4.PNG壓縮
/**壓縮png */
function compressOnePNG(filePath, completeHandler) {
const exePath = "./lib/pngquant/pngquant.exe";
let param = ['--strip', '--force'];
param.push('--skip-if-larger');
param.push("--quality=" + [pngQualityLow, pngQualityHigh].join("-"));
param.push('--output', filePath);
param.push(filePath);
let childP = child_process.spawn(exePath, param);
childP.stdout.on('data', function (data) {
});
childP.stderr.on('data', function (data) {
});
childP.on("close", function (code) {
let iCode = parseInt(code);
if (iCode != 0) {//error
}
console.log(filePath, "壓縮完成");
completeHandler && completeHandler(filePath);
});
}
5.JPG壓縮
/**壓縮jpg */
function compressOneJPG(url, completeHandler) {
let exePath = "guetzli_" + os.platform() + "_x86";
if (os.arch == "x64") {
exePath += "-64";
}
if ("win32" == os.platform()) {
exePath += ".exe";
}
console.log("開始壓縮:",url);
exePath = path.join("./lib/guetzli/", exePath);
var params = ["--quality", jpgQuality];
params.push(url, url);
var childP = child_process.spawn(exePath, params);
childP.stdout.on('data', function (data) {
console.log("stdout:", data);
});
childP.stderr.on('data', function (data) {
console.error("stderr:", data);
console.error("compress fail pic:", url);
});
childP.on('close', function (code) {
console.log("壓縮完成:",url)
if (completeHandler != null) {
completeHandler(url);
}
});
}
工具使用說明
1.修改conf目錄下的conf.json文件,將root_path配置正確(執行需要壓縮圖片的目錄)
2.運行前執行npm install 命令安裝依賴,目前就只用到md5模塊。
下載路徑:https://download.csdn.net/download/weixin_36719607/12238348