japi/apps/jvector/service.py
2025-05-12 02:39:56 +08:00

229 lines
7.5 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

import io
import os
import requests
import traceback
import tempfile
import base64
from urllib.parse import urlparse
from pathlib import Path
from PIL import Image
import asyncio
from settings import settings
class JvectorService:
def __init__(self):
"""初始化矢量图转换服务"""
# 获取配置变量
self.upload_url = settings.upload_url
self.vector_api_id = settings.vector_api_id
self.vector_api_secret = settings.vector_api_secret
self.vector_mode = settings.vector_mode
def _get_config(self, key):
"""获取配置值,从环境变量读取"""
if key == "upload_url":
return settings.upload_url
# 其他配置项的处理方式
config_map = {}
return config_map.get(key, "")
def upload_image_to_intermediate_server(self, image_url):
"""上传图片到中转服务器的函数"""
try:
response = requests.get(image_url, verify=False)
response.raise_for_status()
image_data = response.content
parsed_url = urlparse(image_url)
file_name = Path(parsed_url.path).name
file_ext = Path(file_name).suffix
# 如果图片是webp格式转换为png格式
if file_ext.lower() == '.webp':
image = Image.open(io.BytesIO(image_data))
png_buffer = io.BytesIO()
image.save(png_buffer, format='PNG')
image_data = png_buffer.getvalue()
file_name = file_name.replace('.webp', '.png')
files = {"file": (file_name, image_data)}
upload_response = requests.post(self.upload_url, files=files, verify=False)
if upload_response.status_code == 200:
return upload_response.json()["file_url"]
else:
error_msg = f"上传失败. 状态码: {upload_response.status_code}, {upload_response.text}"
print(error_msg)
raise Exception(error_msg)
except Exception as e:
error_msg = f"上传图像到中间服务器失败: {str(e)}, URL: {image_url}"
print(error_msg)
traceback.print_exc()
raise Exception(error_msg)
def convert_image_to_vector(self, image_url):
"""将图片转换为矢量图的函数"""
try:
url = "https://vectorizer.ai/api/v1/vectorize"
data = {
'image.url': image_url,
'mode': self.vector_mode
}
auth = (self.vector_api_id, self.vector_api_secret)
response = requests.post(url, data=data, auth=auth)
response.raise_for_status()
return response.content
except Exception as e:
error_msg = f"转换图像为矢量图失败: {str(e)}, URL: {image_url}"
print(error_msg)
traceback.print_exc()
raise Exception(error_msg)
def svg_to_base64(self, svg_content):
"""将SVG内容转换为base64字符串"""
return base64.b64encode(svg_content).decode('utf-8')
async def vectorize_image(self, image_url):
"""
将图片转换为矢量图
Args:
image_url: 输入图像的URL
Returns:
处理后的矢量图内容
"""
try:
# 转换为矢量图
vector_content = self.convert_image_to_vector(image_url)
# 转换为base64
svg_content = self.svg_to_base64(vector_content)
return {
"status": "success",
"svg_content": svg_content
}
except Exception as e:
raise Exception(f"矢量图转换失败: {str(e)}")
async def vectorize_from_file(self, file_content):
"""
从上传的文件内容创建矢量图
Args:
file_content: 上传的文件内容
Returns:
处理后的矢量图内容
"""
temp_file = None
try:
# 创建临时文件
temp_file = tempfile.NamedTemporaryFile(delete=False, suffix='.png')
with open(temp_file.name, 'wb') as f:
f.write(file_content)
# 上传到中转服务器
with open(temp_file.name, 'rb') as f:
files = {"file": ("image.png", f)}
upload_response = requests.post(self.upload_url, files=files, verify=False)
if upload_response.status_code == 200:
intermediate_url = upload_response.json()["file_url"]
else:
raise Exception(f"上传失败. 状态码: {upload_response.status_code}")
# 转换为矢量图
vector_content = self.convert_image_to_vector(intermediate_url)
# 转换为base64
svg_content = self.svg_to_base64(vector_content)
return {
"status": "success",
"svg_content": svg_content
}
except Exception as e:
raise Exception(f"处理文件失败: {str(e)}")
finally:
# 清理临时文件
if temp_file and os.path.exists(temp_file.name):
try:
os.unlink(temp_file.name)
except:
pass
async def process_batch(self, urls):
"""
批量处理多个URL图像流式返回结果
Args:
urls: 图片URL列表
Yields:
每个图片的处理结果
"""
total = len(urls)
success_count = 0
error_count = 0
for i, url in enumerate(urls, 1):
try:
url_str = str(url)
result = await self.vectorize_image(url_str)
success_count += 1
# 确保返回正确的数据格式
yield {
"index": i,
"total": total,
"original_url": url_str,
"status": "success",
"svg_content": result["svg_content"],
"success_count": success_count,
"error_count": error_count,
"message": "处理成功"
}
except Exception as e:
error_count += 1
yield {
"index": i,
"total": total,
"original_url": str(url),
"status": "error",
"error": str(e),
"success_count": success_count,
"error_count": error_count,
"message": f"处理失败: {str(e)}"
}
# 让出控制权,避免阻塞
await asyncio.sleep(0)
def is_valid_url(self, url):
"""验证URL是否有效"""
try:
result = urlparse(url)
return all([result.scheme, result.netloc])
except:
return False
def download_file(self, url, filename):
"""下载文件到本地"""
response = requests.get(url, stream=True)
response.raise_for_status()
with open(filename, 'wb') as f:
for chunk in response.iter_content(chunk_size=8192):
f.write(chunk)
return filename