From 670e9f1ec3c2b980f1504f39615b6739131bc94f Mon Sep 17 00:00:00 2001 From: HuangHai <10402852@qq.com> Date: Tue, 19 Aug 2025 15:06:49 +0800 Subject: [PATCH] 'commit' --- .../com/dsideal/Ai/Util/JiMeng/JmTxt2Img.py | 89 ++++++++ .../dsideal/Ai/Util/JiMeng/Kit/JmCommon.py | 193 ++++++++++++++++++ dsLightRag/JiMeng/JmTxt2Img.py | 9 +- dsLightRag/JiMeng/Kit/JmCommon.py | 22 +- .../Kit/__pycache__/JmCommon.cpython-310.pyc | Bin 5212 -> 0 bytes 5 files changed, 300 insertions(+), 13 deletions(-) create mode 100644 dsAi/src/main/python/com/dsideal/Ai/Util/JiMeng/JmTxt2Img.py create mode 100644 dsAi/src/main/python/com/dsideal/Ai/Util/JiMeng/Kit/JmCommon.py delete mode 100644 dsLightRag/JiMeng/Kit/__pycache__/JmCommon.cpython-310.pyc diff --git a/dsAi/src/main/python/com/dsideal/Ai/Util/JiMeng/JmTxt2Img.py b/dsAi/src/main/python/com/dsideal/Ai/Util/JiMeng/JmTxt2Img.py new file mode 100644 index 00000000..5018ee71 --- /dev/null +++ b/dsAi/src/main/python/com/dsideal/Ai/Util/JiMeng/JmTxt2Img.py @@ -0,0 +1,89 @@ +import json +import os +import time +import base64 +from com.dsideal.Ai.Util.JiMeng.Kit.JmCommon import JmCommon +from com.dsideal.Ai.Util.JiMeng.Kit.JmErrorCode import JmErrorCode + + +class JmTxt2Img: + action = "CVProcess" + req_key = "jimeng_high_aes_general_v21_L" + + @staticmethod + def generate_image(prompt, save_img_path): + """生成图片""" + # 创建请求体 + req = { + "req_key": JmTxt2Img.req_key, + "prompt": prompt + } + + response_body = JmCommon.do_request("POST", {}, json.dumps(req).encode('utf-8'), JmTxt2Img.action) + jo = json.loads(response_body) + + # 检查响应状态码 + code = jo.get("code") + if not JmErrorCode.is_success(code): + error_msg = JmErrorCode.get_message_by_code(code) + print(f"生成图片失败: 错误码={code}, 错误信息={error_msg}") + raise Exception(f"生成图片失败: {error_msg}") + + # 获取图片Base64数据 + img_base64 = jo.get("data", {}).get("binary_data_base64", [""])[0] + + # 确保目录存在 + os.makedirs(os.path.dirname(save_img_path), exist_ok=True) + + # 对 Base64 字符串进行解码并保存为文件 + try: + # 注意:有些Base64字符串可能有前缀,需要去除 + if img_base64.startswith('data:image'): + img_base64 = img_base64.split(',')[1] + bytes_data = base64.b64decode(img_base64) + with open(save_img_path, 'wb') as f: + f.write(bytes_data) + print(f"文件保存成功!文件位置: {save_img_path}") + except Exception as e: + print(f"保存图片失败: {str(e)}") + raise Exception(f"保存图片失败: {str(e)}") + + @staticmethod + def main(): + """主函数,用于测试""" + # 示例提示词 + prompt = "鸿门宴,室内场景图。非真人,是一幅画。远景镜头,让人一看就知道是在讲鸿门宴," + prompt += "1、要亮!" + prompt += "2、不要有人出现" + prompt += "3、有大厅、墙面,桌子,不要有台阶,否则项庄没机会刺杀刘邦" + prompt += "4、西周时期特点" + prompt += "5、宏大,雄伟,颜色鲜明,不要灰色调的,不要有雾霾之类的" + prompt += "6、超高清画质" + + # 保存图片路径 - 使用绝对路径避免相对路径问题 + save_image_path = os.path.abspath(os.path.join(os.path.dirname(__file__), "..", "..", "..", "..", "..", "..", "Text2Img.jpg")) + print(f"保存图片路径:{save_image_path}") + + # 添加重试逻辑,处理API并发限制错误 + retry_count = 0 + max_retries = 5 # 减少重试次数以便更快排查问题 + retry_interval = 5000 # 重试间隔(毫秒) + + while True: + try: + JmTxt2Img.generate_image(prompt, save_image_path) + # 成功生成图片,跳出循环 + break + except Exception as e: + print(f"生成图片异常: {str(e)}") + retry_count += 1 + if retry_count < max_retries: + print(f"等待{retry_interval}毫秒后重试...") + time.sleep(retry_interval / 1000) + else: + print(f"已达到最大重试次数: {max_retries}") + raise e # 达到最大重试次数,抛出异常 + + +if __name__ == "__main__": + JmTxt2Img.main() \ No newline at end of file diff --git a/dsAi/src/main/python/com/dsideal/Ai/Util/JiMeng/Kit/JmCommon.py b/dsAi/src/main/python/com/dsideal/Ai/Util/JiMeng/Kit/JmCommon.py new file mode 100644 index 00000000..4b1288d5 --- /dev/null +++ b/dsAi/src/main/python/com/dsideal/Ai/Util/JiMeng/Kit/JmCommon.py @@ -0,0 +1,193 @@ +import hashlib +import hmac +import json +import os +import time +from datetime import datetime, timezone +import requests +import base64 + + +class JmCommon: + # 请求域名和相关配置 + host = "visual.volcengineapi.com" + path = "/" + service = "cv" + region = "cn-north-1" + schema = "https" + version = "2022-08-31" + + # API访问凭证 - 请替换为您自己的凭证 + ak = "AKLTZjVlOGU1NzA1YWZkNDExMzkzYzY5YTNlOTRmMTMxODg" + sk = "WkdabU9UTXdNVEJpTmpWbE5HVTJZVGxtTnpWbU5XSTBaRGN5TW1NMk5tRQ==" + + # 项目路径 + project_root = os.path.abspath(os.path.join(os.path.dirname(__file__), "..", "..", "..", "..", "..", "..")) + base_path = os.path.join(project_root, "src", "main", "python", "com", "dsideal", "aiSupport", "Util", "JiMeng", "Example") + + @staticmethod + def sign_string_encoder(source): + """URL编码字符串,用于签名""" + if source is None: + return None + + # 不需要编码的字符 + safe_chars = set("abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789-_.~") + result = [] + + for char in source: + if char in safe_chars: + result.append(char) + elif char == ' ': + result.append("%20") + else: + # 对其他字符进行URL编码 + result.append(f"%{ord(char):02X}") + + return ''.join(result) + + @staticmethod + def hash_sha256(content): + """计算内容的SHA256哈希值""" + try: + md = hashlib.sha256() + md.update(content) + return md.hexdigest() + except Exception as e: + raise Exception(f"计算哈希值失败: {str(e)}") + + @staticmethod + def hmac_sha256(key, content): + """使用HMAC-SHA256算法计算消息认证码""" + try: + h = hmac.new(key, content.encode('utf-8'), hashlib.sha256) + return h.digest() + except Exception as e: + raise Exception(f"计算HMAC-SHA256失败: {str(e)}") + + @staticmethod + def gen_signing_secret_key_v4(date, region, service): + """生成V4版本的签名密钥""" + try: + # 解码SK (Base64) + sk_bytes = base64.b64decode(JmCommon.sk) + k_date = JmCommon.hmac_sha256(sk_bytes, date) + k_region = JmCommon.hmac_sha256(k_date, region) + k_service = JmCommon.hmac_sha256(k_region, service) + k_signing = JmCommon.hmac_sha256(k_service, "request") + return k_signing + except Exception as e: + raise Exception(f"生成签名密钥失败: {str(e)}") + + @staticmethod + def do_request(method, query_list, body, action): + """发送HTTP请求到火山引擎API""" + date = datetime.now(timezone.utc) + if body is None: + body = b'' + + # 计算请求体的SHA256哈希值 + x_content_sha256 = JmCommon.hash_sha256(body) + + # 格式化日期时间,用于请求头 + x_date = date.strftime("%Y%m%dT%H%M%SZ") + short_x_date = x_date[:8] # 提取日期部分,用于凭证范围 + + content_type = "application/json" + sign_header = "host;x-date;x-content-sha256;content-type" + + # 构建查询参数字符串 + real_query_list = query_list.copy() if query_list else {} + real_query_list["Action"] = action + real_query_list["Version"] = JmCommon.version + + # 排序查询参数 + sorted_query = sorted(real_query_list.items()) + query_sb = [] + for key, value in sorted_query: + encoded_key = JmCommon.sign_string_encoder(key) + encoded_value = JmCommon.sign_string_encoder(str(value)) + query_sb.append(f"{encoded_key}={encoded_value}") + query_string = "&" .join(query_sb) + + # 构建规范请求字符串,用于签名 + canonical_string = f"{method}\n{JmCommon.path}\n{query_string}\n" + canonical_string += f"host:{JmCommon.host}\n" + canonical_string += f"x-date:{x_date}\n" + canonical_string += f"x-content-sha256:{x_content_sha256}\n" + canonical_string += f"content-type:{content_type}\n\n" + canonical_string += f"{sign_header}\n{x_content_sha256}" + + # 计算规范请求字符串的哈希值 + hash_canonical_string = JmCommon.hash_sha256(canonical_string.encode('utf-8')) + + # 构建凭证范围和签名字符串 + credential_scope = f"{short_x_date}/{JmCommon.region}/{JmCommon.service}/request" + sign_string = f"HMAC-SHA256\n{x_date}\n{credential_scope}\n{hash_canonical_string}" + + # 生成签名密钥并计算签名 + sign_key = JmCommon.gen_signing_secret_key_v4(short_x_date, JmCommon.region, JmCommon.service) + signature = hmac.new(sign_key, sign_string.encode('utf-8'), hashlib.sha256).hexdigest() + + # 构建URL + url = f"{JmCommon.schema}://{JmCommon.host}{JmCommon.path}?{query_string}" + + # 设置请求头 + headers = { + "Host": JmCommon.host, + "X-Date": x_date, + "X-Content-Sha256": x_content_sha256, + "Content-Type": content_type, + "Authorization": f"HMAC-SHA256 Credential={JmCommon.ak}/{credential_scope}, SignedHeaders={sign_header}, Signature={signature}" + } + + # 发送请求 + try: + print(f"请求URL: {url}") + print(f"请求头: {headers}") + response = requests.request( + method=method, + url=url, + headers=headers, + data=body, + timeout=(30, 30) # 连接超时和读取超时 + ) + print(f"响应状态码: {response.status_code}") + print(f"响应内容: {response.text}") + response.raise_for_status() # 如果状态码不是200,抛出异常 + return response.text + except requests.exceptions.RequestException as e: + raise Exception(f"API请求失败: {str(e)}") + + @staticmethod + def download_file(file_url, save_file_path): + """从URL下载文件到指定路径""" + try: + # 确保目录存在 + os.makedirs(os.path.dirname(save_file_path), exist_ok=True) + + # 下载文件 + response = requests.get(file_url, timeout=30) + response.raise_for_status() + + with open(save_file_path, 'wb') as f: + f.write(response.content) + + file_size = os.path.getsize(save_file_path) + print(f"文件下载成功,保存路径: {save_file_path}, 文件大小: {file_size}字节") + except Exception as e: + print(f"文件下载失败: {str(e)}") + raise Exception(f"文件下载失败: {str(e)}") + + @staticmethod + def query_task_result(task_id): + """查询异步任务结果""" + req_key = "jimeng_vgfm_t2v_l20" + action = "CVSync2AsyncGetResult" + # 创建请求体 + req = { + "task_id": task_id, + "req_key": req_key + } + response_body = JmCommon.do_request("POST", {}, json.dumps(req).encode('utf-8'), action) + return json.loads(response_body) \ No newline at end of file diff --git a/dsLightRag/JiMeng/JmTxt2Img.py b/dsLightRag/JiMeng/JmTxt2Img.py index 4a491610..a74bf869 100644 --- a/dsLightRag/JiMeng/JmTxt2Img.py +++ b/dsLightRag/JiMeng/JmTxt2Img.py @@ -3,9 +3,6 @@ import os import time import base64 -from JiMeng.Kit.JmCommon import JmCommon -from JiMeng.Kit.JmErrorCode import JmErrorCode - class JmTxt2Img: action = "CVProcess" @@ -61,13 +58,13 @@ class JmTxt2Img: prompt += "5、宏大,雄伟,颜色鲜明,不要灰色调的,不要有雾霾之类的" prompt += "6、超高清画质" - # 保存图片路径 - save_image_path = os.path.join(JmCommon.base_path, "Text2Img.jpg") + # 保存图片路径 - 使用绝对路径避免相对路径问题 + save_image_path = os.path.abspath(os.path.join(os.path.dirname(__file__), "..", "..", "..", "..", "..", "..", "Text2Img.jpg")) print(f"保存图片路径:{save_image_path}") # 添加重试逻辑,处理API并发限制错误 retry_count = 0 - max_retries = 1000 # 最大重试次数 + max_retries = 5 # 减少重试次数以便更快排查问题 retry_interval = 5000 # 重试间隔(毫秒) while True: diff --git a/dsLightRag/JiMeng/Kit/JmCommon.py b/dsLightRag/JiMeng/Kit/JmCommon.py index 41824a71..4b1288d5 100644 --- a/dsLightRag/JiMeng/Kit/JmCommon.py +++ b/dsLightRag/JiMeng/Kit/JmCommon.py @@ -17,7 +17,7 @@ class JmCommon: schema = "https" version = "2022-08-31" - # API访问凭证 + # API访问凭证 - 请替换为您自己的凭证 ak = "AKLTZjVlOGU1NzA1YWZkNDExMzkzYzY5YTNlOTRmMTMxODg" sk = "WkdabU9UTXdNVEJpTmpWbE5HVTJZVGxtTnpWbU5XSTBaRGN5TW1NMk5tRQ==" @@ -68,12 +68,16 @@ class JmCommon: @staticmethod def gen_signing_secret_key_v4(date, region, service): """生成V4版本的签名密钥""" - # 注意:这里sk需要解码,因为Java代码中sk是Base64编码的 - sk_bytes = base64.b64decode(JmCommon.sk) - k_date = JmCommon.hmac_sha256(sk_bytes, date) - k_region = JmCommon.hmac_sha256(k_date, region) - k_service = JmCommon.hmac_sha256(k_region, service) - return JmCommon.hmac_sha256(k_service, "request") + try: + # 解码SK (Base64) + sk_bytes = base64.b64decode(JmCommon.sk) + k_date = JmCommon.hmac_sha256(sk_bytes, date) + k_region = JmCommon.hmac_sha256(k_date, region) + k_service = JmCommon.hmac_sha256(k_region, service) + k_signing = JmCommon.hmac_sha256(k_service, "request") + return k_signing + except Exception as e: + raise Exception(f"生成签名密钥失败: {str(e)}") @staticmethod def do_request(method, query_list, body, action): @@ -139,6 +143,8 @@ class JmCommon: # 发送请求 try: + print(f"请求URL: {url}") + print(f"请求头: {headers}") response = requests.request( method=method, url=url, @@ -146,6 +152,8 @@ class JmCommon: data=body, timeout=(30, 30) # 连接超时和读取超时 ) + print(f"响应状态码: {response.status_code}") + print(f"响应内容: {response.text}") response.raise_for_status() # 如果状态码不是200,抛出异常 return response.text except requests.exceptions.RequestException as e: diff --git a/dsLightRag/JiMeng/Kit/__pycache__/JmCommon.cpython-310.pyc b/dsLightRag/JiMeng/Kit/__pycache__/JmCommon.cpython-310.pyc deleted file mode 100644 index a215ef8800ff8815e4bc5ddd6648ce7dcfb455eb..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 5212 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 CZQs`b