Browse Source

KERNEL-19353 add:图片导出

main
codingXiaxw 1 month ago
commit
3a237f6c36
  1. 36
      Dockerfile
  2. 6
      README.md
  3. 3003
      package-lock.json
  4. 16
      package.json
  5. 16
      src/exportController.js
  6. 24
      src/exportHandler.js
  7. 74
      src/export_server.js
  8. 54
      src/puppeteerService.js

36
Dockerfile

@ -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"]

6
README.md

@ -0,0 +1,6 @@
## 导出图片服务
### 启动
```
docker build -t exporter
docker run -p 3000:3000 exporter
```

3003
package-lock.json generated

File diff suppressed because it is too large Load Diff

16
package.json

@ -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"
}
}

16
src/exportController.js

@ -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}/`);
});

24
src/exportHandler.js

@ -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
};

74
src/export_server.js

@ -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}/`);
});

54
src/puppeteerService.js

@ -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…
Cancel
Save