node批量处理大量图片

因为瓦片地图的颜色问题,需要对大量地图图片进行颜色处理。前端处理可以利用canvas,然后转为Base64,调用接口保存,但是需要前后端配合,递归读取文件,处理后进行保存,由于chrome对同一server的http请求数量最多只有6个,限制执行效率,所以采取纯服务的方式。

图片处理依赖node-canvas库,最开始采用异步IO的方式,导致程序启动后读取所有文件(7w张图片),程序崩溃,后面改为同步方式,并采用多进程单线程负载均衡,处理7w张图片耗时500s左右。

工程包括 config.js配置文件,master.js主进程文件,worker.js工作进程文件。

config.js:

module.exports = {
    
    
    SRC_DIR: 'D:\\TIANDITU2\\TIANDITU2', // 源文件目录
    TARGER_DIR: 'resultImages' // 输出目录
}

master.js:

const fs = require('fs');
const cpuNum = require('os').cpus().length;
const childProcess = require('child_process');

const CONFIG = require('./config');
let index = 0; // 负载均衡
let workers = []; // 子进程
let pids = []; // 子进程pid
let count = 0; // 已处理文件数量
let timeStamp = +new Date(); // 启动时间戳

// 创建子进程
createWorkers();
// 读取文件,由子进程处理
getFiles(CONFIG.SRC_DIR)

function getFiles(dir) {
    
    
	fs.readdir(dir, function(err, files){
    
    
		if(err) {
    
    
			console.warn(err)
		}else {
    
    
			files.forEach(fileName => {
    
    
                workers[index].send({
    
    dir, fileName})
                index = Number.parseInt((index + 1) % cpuNum)
			})
		}
	})
}

function createWorkers() {
    
    
    console.log('-------------------- 主进程启动 --------------------')
    for (let i = 0; i < cpuNum; ++i) {
    
    
        let worker_instance = childProcess.fork('worker.js');
        worker_instance.on('message', ({
     
     type, data, pid}) => {
    
    
            switch (type) {
    
    
                case 'dir':
                    getFiles(data);
                    break;
                case 'count':
                    console.log('%c已转换' + ++count + '个, 耗时:' + ((+new Date() - timeStamp) / 1000) + 's, 进程' + (pids.indexOf(pid) + 1) + '处理', 'color: green');
                    break;
            }
        })
        workers.push(worker_instance);
        pids.push(worker_instance.pid)
    }
    console.log('-------------------- 子进程启动 --------------------')
}

worker.js:

const path = require("path");
const fs = require('fs');
const {
    
     createCanvas, loadImage } = require('canvas')
const CONFIG = require('./config')

let canvas = null,
    ctx = null,
    canvasWidth = 0,
    canvasHeight = 0;

process.on('message', ({
     
     dir, fileName}) => {
    
    
    var filedir = path.join(dir, fileName);
    let stat = fs.statSync(filedir)
    if(!stat){
    
    
        console.warn('stat');
    }else {
    
    
        var isFile = stat.isFile();
        // 图片 => 转换
        if(isFile) {
    
    
            let dirList = path.dirname(filedir).split(path.sep);
            dirList.shift()
            handleImage(filedir, dirList.join(path.sep), fileName)
        }else {
    
    
            // 文件夹 => 递归获取
            process.send({
    
    type: 'dir', data: filedir})
        }
    }
})

function handleImage(filePath, middleDir, fileName) {
    
    
	loadImage(filePath).then((image) => {
    
    
        // const canvas = createCanvas(image.width, image.height);
        // const ctx = canvas.getContext('2d');
        if(image.width != canvasWidth || image.height != canvasHeight) {
    
    
            canvas = createCanvas(image.width, image.height);
            ctx = canvas.getContext('2d');
            canvasWidth = image.width;
            canvasHeight = image.height;
        }
        ctx.clearRect(0, 0, canvasWidth, canvasHeight);
		// ctx.filter = "hue-rotate(185deg) invert(90%) sepia(30%) saturate(160%) brightness(80%) contrast(90%) grayscale(5%)";
	  	ctx.drawImage(image, 0, 0)
		  
		var imgData = ctx.getImageData(0, 0, canvas.width, canvas.height);
		var px = imgData.data;
		for(var i = 0; i < canvas.width * canvas.height; i++){
    
    
			px[i * 4 + 0] ^= 0xff;
			px[i * 4 + 1] ^= 0xff;
			px[i * 4 + 2] ^= 0xff;
		}
		ctx.clearRect(0, 0, canvas.width, canvas.height);
		ctx.putImageData(imgData, 0, 0);
		
		let dir = path.join(CONFIG.TARGER_DIR, middleDir, fileName);
        dirExists(dir)
          
        fs.writeFileSync(dir, canvas.toBuffer())
        process.send({
    
    type: 'count', pid: process.pid})
	})
}

// 递归判断目录是否存在, 不存在则创建目录
function dirExists(_path) {
    
    
    // 递归判断目录是否存在
    let _dir = path.parse(_path).dir;
    if(fs.existsSync(_dir)) {
    
    
        return true
    }else {
    
    
        // 当前目录不存在,递归校验上层目录
        if( dirExists(_dir) ) {
    
    
            fs.mkdirSync(_dir);
            return true;
        }
    }
}

猜你喜欢

转载自blog.csdn.net/PGD_607/article/details/120363192
今日推荐