commit
3a237f6c36
8 changed files with 3229 additions and 0 deletions
@ -0,0 +1,36 @@
|
||||
# 第一阶段:构建环境 |
||||
FROM node:20.18.3 AS builder |
||||
|
||||
WORKDIR /app |
||||
|
||||
# 复制 package.json 和 package-lock.json |
||||
COPY package*.json ./ |
||||
|
||||
# 安装项目依赖 |
||||
RUN npm install |
||||
|
||||
# 复制项目源代码 |
||||
COPY . . |
||||
|
||||
# 第二阶段:运行环境 |
||||
FROM node:20.18.3 |
||||
|
||||
# 安装 Puppeteer 依赖 |
||||
RUN apt-get update && apt-get install -y \ |
||||
gconf-service libasound2 libatk1.0-0 libc6 libcairo2 libcups2 libdbus-1-3 \ |
||||
libexpat1 libfontconfig1 libgcc1 libgconf-2-4 libgdk-pixbuf2.0-0 libglib2.0-0 \ |
||||
libgtk-3-0 libnspr4 libpango-1.0-0 libpangocairo-1.0-0 libstdc++6 libx11-6 libx11-xcb1 \ |
||||
libxcb1 libxcomposite1 libxcursor1 libxdamage1 libxext6 libxfixes3 libxi6 libxrandr2 \ |
||||
libxrender1 libxss1 libxtst6 ca-certificates fonts-liberation libappindicator1 \ |
||||
libnss3 lsb-release xdg-utils wget |
||||
|
||||
WORKDIR /app |
||||
|
||||
# 从构建环境中复制安装好的依赖和编译后的代码 |
||||
COPY --from=builder /app . |
||||
|
||||
# 暴露容器内的端口 |
||||
EXPOSE 3000 |
||||
|
||||
# 指定容器启动时要执行的命令 |
||||
CMD ["node", "exportController.js"] |
@ -0,0 +1,6 @@
|
||||
## 导出图片服务 |
||||
### 启动 |
||||
``` |
||||
docker build -t exporter |
||||
docker run -p 3000:3000 exporter |
||||
``` |
@ -0,0 +1,16 @@
|
||||
{ |
||||
"name": "my-node-service", |
||||
"version": "1.0.0", |
||||
"description": "", |
||||
"main": "exportController.js", |
||||
"scripts": { |
||||
"test": "echo \"Error: no test specified\" && exit 1" |
||||
}, |
||||
"keywords": [], |
||||
"author": "", |
||||
"license": "ISC", |
||||
"dependencies": { |
||||
"express": "^4.17.2", |
||||
"puppeteer": "^24.1.0" |
||||
} |
||||
} |
@ -0,0 +1,16 @@
|
||||
const express = require('express'); |
||||
const puppeteer = require('puppeteer'); |
||||
const app = express(); |
||||
const port = process.env.PORT || 3000; |
||||
|
||||
app.use(express.json()); |
||||
|
||||
// 引入 exportHandler
|
||||
const exportHandler = require('./exportHandler'); |
||||
|
||||
// 设置路由
|
||||
app.post('/chart/export', exportHandler.handleExport); |
||||
|
||||
app.listen(port, () => { |
||||
console.log(`Server running at http://localhost:${port}/`); |
||||
}); |
@ -0,0 +1,24 @@
|
||||
const puppeteerService = require('./puppeteerService'); |
||||
|
||||
const handleExport = async (req, res) => { |
||||
try { |
||||
const data = req.body; |
||||
const token = data.token; |
||||
const tableId = data.tableId; |
||||
const widgetId = data.widgetId; |
||||
const domain = data.domain; |
||||
console.log('data: ',data); |
||||
console.log('domain is', domain); |
||||
|
||||
const screenshotBuffer = await puppeteerService.exportChart(token, tableId, widgetId, domain); |
||||
|
||||
res.status(200).send(`data:image/png;base64,${screenshotBuffer.toString('base64')}`); |
||||
} catch (error) { |
||||
console.error('Error occurred:', error); |
||||
res.status(500).send('An error occurred while processing your request.'); |
||||
} |
||||
}; |
||||
|
||||
module.exports = { |
||||
handleExport |
||||
}; |
@ -0,0 +1,74 @@
|
||||
const express = require('express'); |
||||
const puppeteer = require('puppeteer'); |
||||
const app = express(); |
||||
const port = 3000; |
||||
|
||||
app.use(express.json()); |
||||
|
||||
app.post('/export', async (req, res) => { |
||||
// 获取请求头中的Authorization字段
|
||||
const authHeader = req.headers.authorization; |
||||
const token = authHeader.split(' ')[1]; |
||||
console.log('Token:', token); |
||||
|
||||
const data = req.body; |
||||
const tableId = data.tableId; |
||||
const widgetId = data.widgetId; |
||||
const domain = data.domain; |
||||
console.log(data); |
||||
console.log(domain); |
||||
const browser = await puppeteer.launch({ |
||||
headless: false |
||||
}); |
||||
const page = await browser.newPage(); |
||||
|
||||
// 启用请求拦截
|
||||
await page.setRequestInterception(true); |
||||
|
||||
// 添加拦截请求的处理函数
|
||||
page.on('request', (interceptedRequest) => { |
||||
// 修改请求的headers
|
||||
interceptedRequest.continue({ |
||||
headers: { |
||||
...interceptedRequest.headers(), |
||||
'Authorization': `Bearer ${token}` |
||||
} |
||||
}); |
||||
}); |
||||
const url = `http://localhost/decision/home/analysis/${tableId}`; |
||||
console.log(url); |
||||
await page.goto(url, { waitUntil: 'networkidle2' }); |
||||
|
||||
|
||||
const buttonSelector = `.widget-item-${widgetId}`; |
||||
|
||||
|
||||
try { |
||||
await page.waitForSelector(buttonSelector, { visible: true, timeout: 200000000 }); |
||||
await page.click(buttonSelector); |
||||
} catch (error) { |
||||
console.error(error); |
||||
res.status(500).send(`can not find buttonSelector, selector is ${buttonSelector}`); |
||||
return; |
||||
} |
||||
try { |
||||
const chartSelector = `.wId-${widgetId}`; |
||||
await new Promise(resolve => setTimeout(resolve, 5000)); |
||||
await page.waitForSelector(chartSelector, { visible: true, timeout: 5000 }); |
||||
// 确保图表内容已经加载
|
||||
await page.waitForFunction(selector => document.querySelector(selector).innerHTML !== '', {}, chartSelector); |
||||
const chart = await page.$(chartSelector); |
||||
const chartBox = await chart.boundingBox(); |
||||
const screenshotBuffer = await page.screenshot({ |
||||
clip: chartBox |
||||
}); |
||||
res.status(200).send(`data:image/png;base64,${screenshotBuffer}`); |
||||
} catch (error) { |
||||
console.error(error); |
||||
res.status(500).send(`can not find chartSelector, selector is ${buttonSelector}`); |
||||
} |
||||
}); |
||||
app.listen(port, () => { |
||||
console.log(`Server running at http://localhost:${port}/`); |
||||
}); |
||||
|
@ -0,0 +1,54 @@
|
||||
const puppeteer = require('puppeteer'); |
||||
|
||||
const exportChart = async (token, tableId, widgetId, domain) => { |
||||
const browser = await puppeteer.launch({ |
||||
headless: false, |
||||
defaultViewport: { |
||||
width: 1920, |
||||
height: 1080 |
||||
} |
||||
}); |
||||
const page = await browser.newPage(); |
||||
|
||||
// 启用请求拦截
|
||||
await page.setRequestInterception(true); |
||||
|
||||
// 添加拦截请求的处理函数
|
||||
page.on('request', (interceptedRequest) => { |
||||
// 修改请求的headers
|
||||
interceptedRequest.continue({ |
||||
headers: { |
||||
...interceptedRequest.headers(), |
||||
'Authorization': `Bearer ${token}` |
||||
} |
||||
}); |
||||
}); |
||||
|
||||
const url = `${domain}/decision/home/analysis/${tableId}`; |
||||
console.log(`url is ${url}`) |
||||
await page.goto(url, { waitUntil: 'networkidle2' }); |
||||
|
||||
const buttonSelector = `.widget-item-${widgetId}`; |
||||
await page.waitForSelector(buttonSelector, { visible: true, timeout: 200000000 }); |
||||
await page.click(buttonSelector); |
||||
|
||||
const chartSelector = `.wId-${widgetId}`; |
||||
await new Promise(resolve => setTimeout(resolve, 5000)); |
||||
await page.waitForSelector(chartSelector, { visible: true, timeout: 2000 }); |
||||
// 确保图表内容已经加载
|
||||
await page.waitForFunction(selector => document.querySelector(selector).innerHTML !== '', {}, chartSelector); |
||||
|
||||
const chart = await page.$(chartSelector); |
||||
const chartBox = await chart.boundingBox(); |
||||
const screenshotBuffer = await page.screenshot({ |
||||
encoding: 'base64' , |
||||
clip: chartBox |
||||
}); |
||||
await browser.close(); |
||||
console.log('browser is closed') |
||||
return screenshotBuffer; |
||||
}; |
||||
|
||||
module.exports = { |
||||
exportChart |
||||
}; |
Loading…
Reference in new issue