大家好,欢迎来到IT知识分享网。
因为瓦片地图的颜色问题,需要对大量地图图片进行颜色处理。前端处理可以利用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; } } }
免责声明:本站所有文章内容,图片,视频等均是来源于用户投稿和互联网及文摘转载整编而成,不代表本站观点,不承担相关法律责任。其著作权各归其原作者或其出版社所有。如发现本站有涉嫌抄袭侵权/违法违规的内容,侵犯到您的权益,请在线联系站长,一经查实,本站将立刻删除。 本文来自网络,若有侵权,请联系删除,如若转载,请注明出处:https://haidsoft.com/111926.html