要实现前端的一些聊天记录,生成聊天记录的那种截图,效果如下:
网上看了很多,大部分都没有资源,要么就是仗着一点技术非要挣钱用,我真看不起。
而我的需求比较简单,就是前端将标题、内容、图片、联系方式等发到后端,然后后端生成截图,并返回给前端。
技术路线比较多,我的技术路线是:
前端发起请求--后端java调用python脚本利用selenium生成html的截图,裁剪--上传到七牛云--返回链接给前端。
-
为什么不用canvas? canvas不支持很多css样式,而我前端比较菜,我真调不出来想要的样式。
-
为什么直接用java而要用python? 因为我swing、以及java的绘图也比较菜,用起来打脑壳。
-
为什么用selenium? 首先用html生成聊天记录页是非常简单的一件事情,然后用selenium进行页面截图也是非常简单的。
第一步,首先使用thymeleaf写一个聊天页面图
例子:
<html>
<head>
<meta charSet="utf-8"/>
<meta name="viewport" content="initial-scale=1, maximum-scale=1, user-scalable=no"/>
<title>聊天记录生成页</title>
<style>
body {
background-color: #49270a;
margin: 0;
}
.container {
width: 300px;
min-height: 600px;
background-color: #efdbc9;
display: flex;
flex-direction: column;
align-items: center;
font-family: "微软雅黑";
}
.time {
background-color: aliceblue;
border-radius: 20px;
padding: 2px 10px 2px 10px;
margin-top: 20px;
margin-bottom: 20px;
font-size: 12px;
}
.label {
background-color: aliceblue;
border-radius: 5px;
padding: 2px 10px 2px 10px;
color: blue;
margin-right: auto;
margin-left: 20px;
font-size: 12px;
}
.title {
margin-top: 5px !important;
}
.images img{
max-width: 180px;
}
.public {
margin-right: auto;
margin-left: 20px;
margin-top: 20px;
background-color: aliceblue;
border-radius: 5px;
padding: 10px 20px 10px 20px;
max-width: 180px;
font-size: 14px;
}
.admin-value {
margin-left: auto;
margin-right: 20px;
margin-top: 40px;
background-color: aliceblue;
border-radius: 5px;
padding: 10px 20px 10px 20px;
max-width: 180px;
}
</style>
<script>
</script>
</head>
<body>
<div class="container">
<div class="time">2024-07-02 13:33:44</div>
<div class="label">#求助</div>
<div class="public title">找会做小程序的人</div>
<div class="public content">墙墙你好,帮我找几个会做微信小程序的人好不好啊?</div>
<div class="public images"><image src="https://image.ruankun.xyz/" mode=""/></div>
<div class="public contact">联系方式:1878355</div>
<div class="admin-value">墙墙已收到,四川农业大学校园墙:pythonbutinghua</div>
</div>
</body>
</html>
是不是so easy,而且多张图片的情况下也能自适应,我这里都用的静态内容,当然你可以在springboot的controller中传入你需要的数据动态生成页面。
第二步、利用python selenium截取页面图
首先你要确保你会使用python调用selenium并截获页面,这个过程本身是非常简单的,但是它存在一个问题,就是截图是整个页面,需要对其进行裁剪,裁出你需要的部分,我的做法是将不需要的部分全部变成一个RGB值,然后截图后裁掉不需要的部分,值得说的是,RGB的值总共有255255 255个,所以一般情况下裁剪的过程中不会出现错误。
例子:
from selenium import webdriver
from selenium.webdriver.chrome.service import Service
from selenium.webdriver.chrome.options import Options
from selenium.webdriver.common.by import By
from selenium.webdriver.support.ui import WebDriverWait
from selenium.webdriver.support import expected_conditions as EC
import time
from selenium import webdriver
from selenium.webdriver.support.ui import WebDriverWait
from selenium.webdriver.support import expected_conditions as EC
from selenium.webdriver.common.by import By
from selenium.webdriver.common.desired_capabilities import DesiredCapabilities
from selenium.webdriver.common.action_chains import ActionChains
from selenium.common.exceptions import NoSuchElementException, TimeoutException
from PIL import Image
import random
import string
def generate_random_string(length=16):
# 定义可选择的字符集,包括所有字母和数字
characters = string.ascii_letters + string.digits
# 使用random.choice从字符集中随机选择字符,并使用join方法将它们连接成一个字符串
random_string = ''.join(random.choice(characters) for _ in range(length))
return random_string
def crop_image_by_color(input_image_path, target_color, output_image_path):
# 打开图像文件
image = Image.open(input_image_path)
# 转换为RGB模式,确保图像包含红、绿、蓝三个颜色通道
image = image.convert('RGB')
# 获取图像的宽度和高度
width, height = image.size
# 初始化x和y坐标为None,用于记录目标颜色的首个位置出现
x_pos = None
y_pos = None
# 遍历图像,从左至右、从上至下搜索目标颜色
for x in range(width):
pixel = image.getpixel((x, 0))
if pixel == target_color:
x_pos = x
break
for y in range(height):
pixel = image.getpixel((0, y))
if pixel == target_color:
y_pos = y
break
# 如果已找到x_pos和y_pos,则无需继续遍历
if x_pos is not None and y_pos is not None:
break
# 根据找到的坐标裁剪图像
cropped_image = image.crop((0, 0, x_pos + 1, y_pos + 1))
# 注意:crop方法的坐标是左闭右开的,因此x_pos和y_pos需要加1以确保包含目标像素
# 保存裁剪后的图像
cropped_image.save(output_image_path)
# get直接返回,不再等待界面加载完成
desired_capabilities = DesiredCapabilities.CHROME
desired_capabilities["pageLoadStrategy"] = "none"
# 设置微软驱动器的环境
options = webdriver.EdgeOptions()
options.use_chromium = True
options.add_argument('--disable-blink-features=AutomationControlled')
options.add_argument('--headless')
# 设置浏览器不加载图片,提高速度
# options.add_experimental_option("prefs", {"profile.managed_default_content_settings.images": 2})
# 创建一个微软驱动器
driver = webdriver.Edge(options=options)
try:
# 打开指定的网页
url = "http://localhost:8080/index"
driver.get(url)
# 等待页面加载完成(可选,根据页面加载情况调整)
# 例如,等待某个元素加载完成
# element_present = EC.presence_of_element_located((By.ID, "someElementId"))
# WebDriverWait(driver, 10).until(element_present)
# 截图并保存
random_str = generate_random_string()
screenshot_path = random_str+ ".png"
driver.save_screenshot(screenshot_path)
crop_image_by_color(random_str + '.png', (73,39,10), random_str + '2.png')
print(random_str)
finally:
# 关闭浏览器
driver.quit()
第三步、在springboot中调用python脚本
在controller或者service中调用执行脚本,并拿到文件名后上传到七牛云,最后将链接返回前端,我这里只跑出调用脚本成功的一步,后面上传并返回链接可以自己尝试写:
@GetMapping("/generateChat")
public ResponseEntity<String> generateChat(){
try {
Process process = Runtime.getRuntime().exec("C:/Users/qkmc/.conda/envs/python310/python.exe D:/IdeaProjects/campuswall/src/main/resources/pythonScript/generateChat.py");
// 读取脚本的输出
BufferedReader reader = new BufferedReader(new InputStreamReader(process.getInputStream()));
String result = "";
String line;
while ((line = reader.readLine()) != null) {
result += line;
}
// 等待进程结束并获取退出状态
int exitCode = process.waitFor();
if (exitCode == 0){
return ResponseEntity.ok(result);
}else {
return ResponseEntity.ok("python异常退出:" + exitCode);
}
} catch (IOException e) {
return ResponseEntity.ok("error");
} catch (InterruptedException e) {
return ResponseEntity.ok("被打断了");
}
}
默认情况下,生成的图片存在根目录下,你可以自己解决这个问题:
测试成功: