From 9a5b0828f7ac30a18b04befb3cbfe2a4225ff51b Mon Sep 17 00:00:00 2001 From: admins Date: Thu, 14 May 2026 00:50:10 +0800 Subject: [PATCH] =?UTF-8?q?fix:=20=E5=A2=9E=E5=BC=BA=E5=9B=BE=E7=89=87?= =?UTF-8?q?=E4=B8=8A=E4=BC=A0=E9=94=99=E8=AF=AF=E5=A4=84=E7=90=86=E5=92=8C?= =?UTF-8?q?=E6=97=A5=E5=BF=97=E8=AE=B0=E5=BD=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- modules/wp_image_handler.py | 20 +- .../wp_image_handler.py.bak.20260514_004840 | 309 ++++++++++++++++++ 2 files changed, 328 insertions(+), 1 deletion(-) create mode 100644 modules/wp_image_handler.py.bak.20260514_004840 diff --git a/modules/wp_image_handler.py b/modules/wp_image_handler.py index 37ee212..04414b3 100644 --- a/modules/wp_image_handler.py +++ b/modules/wp_image_handler.py @@ -76,6 +76,7 @@ class ImageHandler: dict: 上传结果,包含 url, id, filename """ if not os.path.exists(image_path): + self.pl.error(f"图片文件不存在:{image_path}") raise FileNotFoundError(f"图片文件不存在:{image_path}") filename = os.path.basename(image_path) @@ -92,16 +93,21 @@ class ImageHandler: with open(image_path, 'rb') as f: image_content = f.read() + self.dl.debug(f"图片文件大小:{len(image_content)} 字节") + # 获取 content_type content_type = self._get_content_type(image_path) - # 构建请求 + # 构建请求头 - 使用正确的 WordPress 媒体上传格式 headers = { 'Content-Disposition': f'attachment; filename="{filename}"', 'Content-Type': content_type, 'Content-Transfer-Encoding': 'binary' } + self.dl.debug(f"请求头:Content-Type={content_type}") + self.dl.debug(f"请求头:Content-Disposition={headers['Content-Disposition']}") + # 上传图片 response = requests.post( f'{self.wp_url}/wp-json/wp/v2/media', @@ -112,6 +118,9 @@ class ImageHandler: timeout=30 ) + self.dl.debug(f"响应状态码:{response.status_code}") + self.dl.debug(f"响应内容:{response.text[:200]}") + if response.status_code == 201: result = response.json() image_url = result.get('source_url', '') @@ -143,9 +152,18 @@ class ImageHandler: else: error_msg = response.text self.pl.error(f"图片上传失败 - 状态码:{response.status_code}") + self.pl.error(f"错误详情:{error_msg}") self.dl.error(f"上传失败:{error_msg}") raise Exception(f"图片上传失败:{error_msg}") + except requests.exceptions.Timeout: + self.pl.error("图片上传请求超时") + self.dl.error("图片上传请求超时") + raise Exception("图片上传请求超时") + except requests.exceptions.ConnectionError as e: + self.pl.error(f"图片上传连接错误:{str(e)}") + self.dl.error(f"图片上传连接错误:{str(e)}") + raise Exception(f"图片上传连接错误:{str(e)}") except Exception as e: self.pl.error(f"图片上传异常:{str(e)}") self.dl.error(f"上传异常:{str(e)}", exc_info=True) diff --git a/modules/wp_image_handler.py.bak.20260514_004840 b/modules/wp_image_handler.py.bak.20260514_004840 new file mode 100644 index 0000000..37ee212 --- /dev/null +++ b/modules/wp_image_handler.py.bak.20260514_004840 @@ -0,0 +1,309 @@ +#!/usr/bin/env python3 +# -*- coding: utf-8 -*- +""" +WordPress 发布系统 - 图片处理模块 +处理图片保存、上传到 WordPress 媒体库 +""" + +import os +import base64 +import requests +from io import BytesIO +from PIL import Image + +from modules.wp_logger import get_publish_logger, get_debug_logger + +# 基础目录 +BASE_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__))) +TEMP_DIR = os.path.join(BASE_DIR, 'temp') +os.makedirs(TEMP_DIR, exist_ok=True) + + +class ImageHandler: + """图片处理器""" + + def __init__(self, wp_url, wp_user, wp_password): + """ + 初始化图片处理器 + + Args: + wp_url: WordPress 站点 URL + wp_user: WordPress 用户名 + wp_password: WordPress 应用密码 + """ + self.wp_url = wp_url.rstrip('/') + self.wp_user = wp_user + self.wp_password = wp_password + self.uploaded_images = {} # 记录已上传的图片 + + self.pl = get_publish_logger() + self.dl = get_debug_logger() + + def save_temp_image(self, image_data, filename): + """ + 保存图片到临时目录 + + Args: + image_data: 图片二进制数据 + filename: 文件名 + + Returns: + str: 临时文件路径 + """ + temp_path = os.path.join(TEMP_DIR, filename) + + try: + with open(temp_path, 'wb') as f: + f.write(image_data) + + self.dl.debug(f"图片已保存:{temp_path}") + return temp_path + + except Exception as e: + self.dl.error(f"保存图片失败:{str(e)}") + raise + + def upload_image(self, image_path, title=None, alt_text=None): + """ + 上传图片到 WordPress 媒体库 + + Args: + image_path: 图片文件路径 + title: 图片标题 + alt_text: 图片 alt 文本 + + Returns: + dict: 上传结果,包含 url, id, filename + """ + if not os.path.exists(image_path): + raise FileNotFoundError(f"图片文件不存在:{image_path}") + + filename = os.path.basename(image_path) + if not title: + title = os.path.splitext(filename)[0] + if not alt_text: + alt_text = title + + self.pl.info(f"📤 上传图片:{filename}") + self.dl.log_step("上传图片", f"文件:{filename}") + + try: + # 读取图片文件 + with open(image_path, 'rb') as f: + image_content = f.read() + + # 获取 content_type + content_type = self._get_content_type(image_path) + + # 构建请求 + headers = { + 'Content-Disposition': f'attachment; filename="{filename}"', + 'Content-Type': content_type, + 'Content-Transfer-Encoding': 'binary' + } + + # 上传图片 + response = requests.post( + f'{self.wp_url}/wp-json/wp/v2/media', + auth=(self.wp_user, self.wp_password), + headers=headers, + data=image_content, + verify=False, # 跳过 SSL 验证 + timeout=30 + ) + + if response.status_code == 201: + result = response.json() + image_url = result.get('source_url', '') + image_id = result.get('id', 0) + + # 更新图片标题和 alt + self._update_image_meta(image_id, title, alt_text) + + self.pl.success(f"图片上传成功 - ID: {image_id}, URL: {image_url}") + self.dl.log_result("上传结果", { + 'id': image_id, + 'url': image_url, + 'filename': filename + }) + + # 记录已上传的图片 + self.uploaded_images[filename] = { + 'id': image_id, + 'url': image_url, + 'title': title + } + + return { + 'id': image_id, + 'url': image_url, + 'filename': filename, + 'title': title + } + else: + error_msg = response.text + self.pl.error(f"图片上传失败 - 状态码:{response.status_code}") + self.dl.error(f"上传失败:{error_msg}") + raise Exception(f"图片上传失败:{error_msg}") + + except Exception as e: + self.pl.error(f"图片上传异常:{str(e)}") + self.dl.error(f"上传异常:{str(e)}", exc_info=True) + raise + + def upload_images_batch(self, images): + """ + 批量上传图片 + + Args: + images: 图片列表,每个图片包含 data, filename + + Returns: + list: 上传结果列表 + """ + results = [] + + for i, image in enumerate(images): + try: + # 保存图片到临时目录 + temp_path = self.save_temp_image(image['data'], image['filename']) + + # 上传图片 + result = self.upload_image( + temp_path, + title=f"图片 {i+1}", + alt_text=f"文章配图 {i+1}" + ) + + results.append(result) + + # 清理临时文件 + if os.path.exists(temp_path): + os.remove(temp_path) + + except Exception as e: + self.pl.error(f"图片 {i+1} 上传失败:{str(e)}") + results.append({'error': str(e), 'filename': image['filename']}) + + return results + + def generate_image_html(self, image_url, alt_text="", width=None): + """ + 生成图片 HTML 标签 + + Args: + image_url: 图片 URL + alt_text: alt 文本 + width: 图片宽度(可选) + + Returns: + str: HTML img 标签 + """ + style = "max-width: 100%; height: auto; display: block; margin: 16px auto;" + if width: + style += f" max-width: {width}px;" + + html = f'{alt_text}' + return f'
{html}
' + + def generate_featured_image_shortcode(self, image_url, alt_text=""): + """ + 生成特色图片短代码 + + Args: + image_url: 图片 URL + alt_text: alt 文本 + + Returns: + str: 特色图片 HTML + """ + return self.generate_image_html(image_url, alt_text) + + def _update_image_meta(self, image_id, title, alt_text): + """更新图片元数据""" + try: + response = requests.post( + f'{self.wp_url}/wp-json/wp/v2/media/{image_id}', + auth=(self.wp_user, self.wp_password), + json={ + 'title': title, + 'alt_text': alt_text + }, + verify=False, + timeout=10 + ) + if response.status_code == 200: + self.dl.debug(f"图片元数据更新成功:ID {image_id}") + except Exception as e: + self.dl.warning(f"更新图片元数据失败:{str(e)}") + + def _get_content_type(self, file_path): + """获取图片 content_type""" + ext = os.path.splitext(file_path)[1].lower() + content_type_map = { + '.jpg': 'image/jpeg', + '.jpeg': 'image/jpeg', + '.png': 'image/png', + '.gif': 'image/gif', + '.bmp': 'image/bmp', + '.webp': 'image/webp', + '.svg': 'image/svg+xml' + } + return content_type_map.get(ext, 'image/jpeg') + + def optimize_image(self, image_path, max_width=1200, quality=85): + """ + 优化图片大小 + + Args: + image_path: 图片路径 + max_width: 最大宽度 + quality: 质量 (1-100) + + Returns: + str: 优化后的图片路径 + """ + try: + img = Image.open(image_path) + + # 获取原始尺寸 + width, height = img.size + + # 如果宽度超过限制,等比例缩放 + if width > max_width: + ratio = max_width / width + new_height = int(height * ratio) + img = img.resize((max_width, new_height), Image.LANCZOS) + self.dl.debug(f"图片已缩放:{width}x{height} -> {max_width}x{new_height}") + + # 保存优化后的图片 + optimized_path = image_path.replace('.', '_optimized.') + img.save(optimized_path, quality=quality, optimize=True) + + return optimized_path + + except Exception as e: + self.dl.warning(f"图片优化失败:{str(e)}") + return image_path + + +def create_image_handler(wp_url, wp_user, wp_password): + """创建图片处理器实例""" + return ImageHandler(wp_url, wp_user, wp_password) + + +if __name__ == '__main__': + import sys + + if len(sys.argv) < 5: + print("用法:python wp_image_handler.py ") + sys.exit(1) + + wp_url = sys.argv[1] + wp_user = sys.argv[2] + wp_password = sys.argv[3] + image_path = sys.argv[4] + + handler = create_image_handler(wp_url, wp_user, wp_password) + result = handler.upload_image(image_path) + print(f"上传结果:{result}")