const fs = require("fs");
const node_path = require("path");
/**
* 数组去重
* @param {*} arr
*/
const distinct = (arr) => {
return Array.from(new Set(arr));
};
class cleanImg {
constructor({
imgFilePath, // 存放图片文件夹的路径
srcFilePath, // src文件夹的路径(源代码所在目录)
writeFilePath, // uselessImg.json 文件目录 (把需要删除的图片路径集合放到 uselessImg.json 文件下)
}) {
this.imgsFilePath = [];
imgFilePath.forEach((item) => {
this.imgsFilePath.push(node_path.resolve(__dirname, item));
});
this.srcFilePath = node_path.resolve(__dirname, srcFilePath);
this.writeFilePath = node_path.resolve(__dirname, writeFilePath);
this.imgFileList = []; //存放图片文件路径的数组
this.srcFileList = []; //文件路径的数组
this.hasImgFlieList = []; // 包含图片的文件数组
this.uselessImg = []; // 没有被使用过的图片
this.imgBeUsedInTheFile = []; // 在文件中使用过的图片集合
this.dirs = [...this.imgsFilePath];
this.srcDirs = [this.srcFilePath];
}
/**
* 判断是不是图片
* @param {String} str 字符串
*/
isImage(str) {
return /\.(png|jpg|gif|jpeg|webp|svg)/.test(str);
}
/**
* 根据路径获取图片名称
* @param {String} path 图片路径
*/
getImgNameByPath(path) {
var filename = "";
//提供特定于平台的路径片段分隔符:
// Windows 上是 \
// POSIX 上是 /
// 例如,在 POSIX 上:
// 'foo/bar/baz'.split(path.sep); // ['foo', 'bar', 'baz']
var slashNum = path.split(path.sep)[0]; // ['D:\\sss\\sss\\ss\\a.png']
if (slashNum.indexOf("\\") > 0) {
// console.log(slashNum.indexOf("\\"), "slashNum.indexOf( \\ )");
slashNum = slashNum.replace(/\\/g, "/"); // ['D:/sss/sss/ss/a.png']
}
slashNum = slashNum.split("/");
// console.log(slashNum, "getImgNameByPath return slashNum");
if (slashNum.length > 0) {
filename = "/" + slashNum[slashNum.length - 1];
} else {
filename = "";
}
console.log(filename, "getImgNameByPath return filename");
return filename;
}
/**
* 处理某个类目下所有文件及目录
* @param files 文件。也可能是目录
* @param file_path 文件或目录的上级目录
* @param callback 一个目录或文件的判断结果的回调
* @param allFilesDoneCallback 所有文件处理完成后的回调函数
*/
forFiles(files, file_path, callback, allFilesDoneCallback) {
var arrlength = files.length;
if (!files || files.length == 0) {
allFilesDoneCallback(file_path);
return;
}
files.forEach(function (e, i) {
var fullFilePath = node_path.join(file_path, e);
fs.stat(fullFilePath, function (err, stat) {
var result = {
isDir: false,
isFile: true,
file: fullFilePath,
};
if (stat.isDirectory()) {
result.isDir = true;
result.isFile = false;
} else {
result.isDir = false;
result.isFile = true;
}
//回调
callback(result);
arrlength--;
//判断是否处理完毕
if (arrlength == 0) {
//回调所有文件处理完毕
allFilesDoneCallback(file_path);
}
});
});
}
/**
* 处理单个目录
* @param dirPath 目录路径
* @param watchDir 监控的目录列表
* @param callback 当目录处理完毕后的回调函数
*/
forDir(dirPath, watchDir, callback) {
var that = this;
fs.readdir(dirPath, function (err, files) {
var subFiles = [];
that.forFiles(
files,
dirPath,
function (result) {
//如果是目录,继续执行forDir并在之前将目录添加到watchDir
//如果是文件,放入subFiles中
if (result.isDir) {
watchDir.push(result.file);
that.forDir(result.file, watchDir, callback);
} else {
subFiles.push(result.file);
}
},
function (processedDirPath) {
//文件全部处理完毕后,执行回调函数通知指定目录遍历完毕,但不包括子目录
callback(processedDirPath, subFiles);
}
);
});
}
/**
* 遍历处理多个类目
* @param dirs 多个类目列表
* @param doneCallback 处理完成的回调
*/
forDirs(dirs, doneCallback) {
var that = this;
var copiedDirs = dirs.slice(0);
var watchDir = [];
var allFiles = [];
copiedDirs.forEach(function (path) {
watchDir.push(path);
//回调函数中判断watchDir长度是否为0,如为0,表示所有的目录及其子目录处理完毕了,通知最外层处理完毕
//并将返回的文件信息合并
that.forDir(path, watchDir, function (processedDirPath, subFiles) {
allFiles = allFiles.concat(subFiles);
// console.log('%s 处理完成',processedDirPath);
watchDir.splice(watchDir.indexOf(processedDirPath), 1);
if (watchDir.length == 0) {
doneCallback(allFiles);
}
});
});
}
/**
* 获取所有图片的路径
*/
getImgFilePath() {
return new Promise((resolve, reject) => {
this.forDirs(this.dirs, function (fileList) {
resolve(fileList);
});
});
}
/**
* 获取src目录下所有文件的路径
*/
getFilePath() {
return new Promise((resolve, reject) => {
this.forDirs(this.srcDirs, function (fileList) {
resolve(fileList);
});
});
}
/**
* 把需要删除的图片路径集合放到 uselessImg.json 文件下,方便查看
*/
creatUselessImgFile() {
fs.writeFile(
this.writeFilePath + "/uselessImg.json",
JSON.stringify(this.uselessImg),
function (err) {
if (err) {
return console.log(err);
}
console.log("The file was saved!");
}
);
}
/**
* 找出没有用到的图片集合
*/
findUselessImgFile() {
let copeImgFileList = [...this.imgFileList]; // cope 一份图片列表
this.imgFileList.forEach((item) => {
// 图片
let imgName = this.getImgNameByPath(item);
// console.log(imgName,'findUselessImgFile imgName');
this.hasImgFlieList.forEach((path) => {
//文件
let fileContent = fs.readFileSync(path, "utf8");
var otherStatus = /[\u4e00-\u9fa5]|\W|\s/g.test(imgName) && fileContent.indexOf(encodeURIComponent(imgName)) >= 0 || fileContent.indexOf(encodeURI(imgName)) >= 0;
if (fileContent.indexOf(imgName) >= 0 || otherStatus) {
let index = copeImgFileList.indexOf(item);
index >= 0 && copeImgFileList.splice(index, 1);
this.imgBeUsedInTheFile.push(item);
}
});
});
this.imgBeUsedInTheFile = distinct(this.imgBeUsedInTheFile); //去重
this.uselessImg = [];
copeImgFileList.forEach((item) => {
console.log(item, "----------copeImgFileList");
if (this.isImage(item)) {
console.log(item, "----------uselessImg");
this.uselessImg.push(item);
}
});
}
/**
* 找出有使用图片的文件集合
*/
findUsedImgFile() {
this.srcFileList.forEach((path) => {
let fileContent = fs.readFileSync(path, "utf8");
this.isImage(fileContent.toString()) && this.hasImgFlieList.push(path);
});
}
/**
* 删除无用图片
*/
removeUselessImg() {
this.uselessImg.forEach((path) => {
if (this.isImage(path)) {
fs.unlink(path, (err) => {
if (err) throw err;
});
}
});
}
async main() {
this.imgFileList = await this.getImgFilePath(); // 获取所有图片的路径
this.srcFileList = await this.getFilePath(); // 获取指定目录下所有文件的路径
console.log("共有图片:", this.imgFileList.length);
console.log("共有文件:", this.srcFileList.length);
this.findUsedImgFile(); // 找出有使用图片的文件集合
console.log("包含图片的文件有:", this.hasImgFlieList.length);
this.findUselessImgFile(); // 找出没有用到的图片
console.log("使用过的图片:", this.imgBeUsedInTheFile.length);
console.log("没有被使用过的图片:", this.uselessImg.length);
this.creatUselessImgFile(); //创建uselessImg.json
this.removeUselessImg(); // 删除无用的图片
}
}
const cleanInstanvce = new cleanImg({
imgFilePath: './src/static/img/', // 存放图片文件夹的路径
srcFilePath: './src', // src文件夹的路径(源代码所在目录)
writeFilePath: './cleanImgHistory' // uselessImg.json 文件目录 (把需要删除的图片路径集合放到 uselessImg.json 文件下)
})
cleanInstanvce.main()
gitee地址