前端场景问题
1、前端开发环境代理和生产环境代理的本质区别。这两个地址代表了在不同环境下的代理目标。
1. Nginx 配置文件中的 proxy_pass
地址
proxy_pass http://172.30.204.241:8372;
- 代表什么:这个地址是 Nginx 反向代理的最终目标地址。
- 用途:这个配置用于生产环境或测试环境。当您的前端项目部署到服务器后,所有来自用户浏览器的
/chat-service
请求,都会被 Nginx 接收,然后无缝地转发到这个地址所指向的后端服务上。 - 网络流向:用户浏览器 -> Nginx 服务器 -> 后端服务(
172.30.204.241:8372
)。
这个地址通常是后端服务的真实内网 IP 和端口,对前端是完全透明的。
2. Vite 配置文件中的 proxy.target
地址
target: http://10.1.115.38:8370,
- 代表什么:这个地址是 Vite 本地开发服务器的代理目标地址。
- 用途:这个配置仅用于本地开发环境。当您在本地运行
npm run dev
时,您的浏览器会向本地的localhost:3000
(或您指定的端口)发起请求,Vite 就会将/chat-service
请求代理到这个地址。 - 网络流向:本地浏览器 -> 本地 Vite 开发服务器 -> 后端服务(
10.1.115.38:8370
)。
为什么这两个地址不同?
这是因为运行环境不同,后端服务的地址也不同。
- Nginx 运行在服务器上,它访问后端服务时,后端可能位于同一个内网段,有自己的内网 IP(如
172.30.204.241
)。 - Vite 运行在您的本地电脑上,它需要访问服务器上的后端服务。所以,它使用的代理地址就是您服务器的公网或内网 IP(
10.1.115.38
),以及后端服务对外暴露的端口(8370
)。
您提供的这两个配置是完全合理的,它们分别服务于不同的场景:前者解决生产环境的跨域问题,后者解决本地开发的跨域问题。
您对这两个代理配置在不同环境下的作用还有疑问吗?
2、无头浏览器截图服务
步骤一:环境准备 (Setup)
首先,确保您的服务器上安装了 Node.js 和 npm。如果还没有,请先安装。
然后,创建一个新的项目文件夹,并初始化它。
mkdir screenshot-service
cd screenshot-service
npm init -y
接着,安装您需要用到的核心库:express
用于创建 API 服务,puppeteer
用于控制无头浏览器。
npm install express puppeteer
步骤二:编写核心服务 (Coding)
创建一个名为 server.js
的文件,将以下代码复制进去。我已经为您添加了详细的注释,解释了每一部分的作用。
const express = require('express');
const puppeteer = require('puppeteer');
const app = express();
const PORT = 3001; // 您可以自定义端口
// 启用对 JSON 请求体的解析
app.use(express.json());
// 启用 CORS,允许您的前端项目访问
app.use((req, res, next) => {
res.header('Access-Control-Allow-Origin', '*'); // 允许所有来源
res.header('Access-Control-Allow-Headers', 'Content-Type');
next();
});
// 定义截图 API 接口
app.post('/api/screenshot', async (req, res) => {
const { url } = req.body;
if (!url) {
return res.status(400).json({ error: 'URL is required' });
}
let browser;
try {
// 启动无头浏览器,添加 --no-sandbox 参数以避免权限问题
browser = await puppeteer.launch({
args: ['--no-sandbox', '--disable-setuid-sandbox']
});
const page = await browser.newPage();
await page.setViewport({ width: 1920, height: 1080 });
// 访问目标 URL 并等待页面加载完成
await page.goto(url, { waitUntil: 'networkidle0' });
// 等待 3D 渲染完成,可以根据实际情况调整时间
// 这一步非常关键,确保所有模型和纹理都已加载
await page.waitForTimeout(5000);
// 执行截图,并以 Base64 格式返回
const screenshot = await page.screenshot({ encoding: 'base64' });
// 返回图片数据给前端
res.json({ imageUrl: `data:image/png;base64,${screenshot}` });
} catch (error) {
console.error('截图失败:', error);
res.status(500).json({ error: 'Failed to capture screenshot' });
} finally {
if (browser) {
await browser.close();
}
}
});
app.listen(PORT, () => {
console.log(`Screenshot service running on port ${PORT}`);
});
步骤三:部署与运行 (Deployment)
- 上传代码:将整个
screenshot-service
文件夹上传到您的 Linux 服务器。 - 安装依赖:在服务器上进入该文件夹,再次运行
npm install
。 - 启动服务:使用
node
命令启动服务。
为了让服务在后台持续运行,我建议您使用node server.js
pm2
等进程管理工具。npm install pm2 -g pm2 start server.js
步骤四:前端调用 (Frontend)
现在,您的截图服务已经部署并运行。您只需修改前端代码,向这个新服务发起请求。
// 在您的 Vue 或 React 项目中
const axios = require('axios');
const urlToCapture = "https://sup.3dsource.cn/#/preview?access_token=...";
axios.post('http://<您的服务器IP>:3001/api/screenshot', { url: urlToCapture })
.then(response => {
const imageUrl = response.data.imageUrl;
// 使用 imageUrl 来显示图片,例如:
const imgElement = document.createElement('img');
imgElement.src = imageUrl;
document.body.appendChild(imgElement);
})
.catch(error => {
console.error('API 调用失败:', error);
});
完成以上步骤,您就能实现一个可靠、高效的截图服务。
实现细节
这段代码能够实现网页截屏,主要是因为它利用了 Puppeteer 这个强大的库。
要理解这段代码的工作原理,可以从以下几个核心点来分析:
1. 启动一个“隐形”浏览器
browser = await puppeteer.launch({
args: ['--no-sandbox', '--disable-setuid-sandbox']
});
puppeteer.launch()
: 这个函数会启动一个真实的、完整的浏览器实例,但它是在后台运行,我们看不到界面,所以被称为“无头浏览器”(Headless Browser)。args
参数: 这些参数是为了解决在某些 Linux 环境下运行时的权限问题,确保服务能够正常启动。
2. 模拟一个浏览器标签页
const page = await browser.newPage();
await page.setViewport({ width: 1920, height: 1080 });
browser.newPage()
: 这行代码的作用是创建一个新的浏览器标签页。我们对网页的所有操作,比如访问 URL、点击、填写表单等,都是在这个page
对象上执行的。setViewport
: 这行代码设置了浏览器窗口的大小,它决定了截图的分辨率。
3. 访问并等待网页加载
await page.goto(url, { waitUntil: 'networkidle0' });
page.goto(url)
: 这行代码指示无头浏览器访问您在curl
请求中提供的url
。waitUntil: 'networkidle0'
: 这是一个非常重要的参数,它告诉 Puppeteer 等待页面加载完成的条件。networkidle0
的意思是:当网络连接数在 500 毫秒内少于或等于 0 时,就认为页面加载完成。这个条件能够确保页面上的所有主要资源(如 HTML、CSS、JavaScript 文件)都已加载。
4. 等待 3D 模型渲染
await new Promise(resolve => setTimeout(resolve, 5000));
由于 3D 模型是动态加载和渲染的,即使网络连接空闲,模型本身可能还需要几秒钟才能完全显示出来。这段代码的作用就是暂停执行 5 秒钟,给 3D 模型预留充足的渲染时间,确保截取的是完整的、已渲染好的画面。
5. 执行截屏并返回数据
const screenshot = await page.screenshot({ encoding: 'base64' });
res.json({ imageUrl: `data:image/png;base64,${screenshot}` });
page.screenshot()
: 这是实现截屏的核心方法。它会截取当前浏览器标签页的完整画面,并将其转换为 Base64 编码的字符串。res.json()
: 最后,后端将这个 Base64 字符串作为 JSON 数据的一部分返回给前端,前端就可以直接解析并显示这张图片了。
简而言之,这段代码就像一个在服务器上工作的“自动化虚拟用户”。它会打开一个隐形的浏览器,访问指定的网页,等待页面内容(特别是 3D 模型)加载并渲染好,然后精准地拍下一张照片,最后将照片数据传回给您。