247 lines
8.5 KiB
Python
247 lines
8.5 KiB
Python
import os
|
||
from urllib.parse import urlparse
|
||
|
||
import requests
|
||
from PyQt6.QtCore import QThread, pyqtSignal
|
||
from PyQt6.QtGui import QImage, QPixmap
|
||
|
||
from app.core import miaoStarsBasicApi
|
||
|
||
|
||
class TextLoaderThread(QThread):
|
||
"""文本文件加载线程"""
|
||
|
||
textLoaded = pyqtSignal(str)
|
||
errorOccurred = pyqtSignal(str)
|
||
progressUpdated = pyqtSignal(int) # 进度更新信号
|
||
|
||
def __init__(self, url):
|
||
super().__init__()
|
||
self.url = url
|
||
|
||
def run(self):
|
||
"""线程执行函数"""
|
||
try:
|
||
# 1. 设置网络请求参数 - 优化连接参数
|
||
session = requests.Session()
|
||
adapter = requests.adapters.HTTPAdapter(
|
||
pool_connections=20,
|
||
pool_maxsize=20,
|
||
max_retries=5, # 增加重试次数
|
||
pool_block=False,
|
||
)
|
||
session.mount("http://", adapter)
|
||
session.mount("https://", adapter)
|
||
|
||
# 2. 增加超时时间并添加重试机制
|
||
response = miaoStarsBasicApi.returnSession().get(
|
||
self.url,
|
||
stream=True,
|
||
timeout=(15, 30), # 增加超时时间:连接15秒,读取30秒
|
||
)
|
||
|
||
response.raise_for_status()
|
||
|
||
# 3. 获取文件大小(如果服务器支持)
|
||
total_size = int(response.headers.get("content-length", 0))
|
||
downloaded_size = 0
|
||
|
||
# 4. 分块读取并处理 - 使用二进制读取提高速度
|
||
content_chunks = []
|
||
for chunk in response.iter_content(chunk_size=16384): # 增大块大小
|
||
if chunk:
|
||
content_chunks.append(chunk)
|
||
downloaded_size += len(chunk)
|
||
|
||
# 更新进度(如果知道总大小)
|
||
if total_size > 0:
|
||
progress = int((downloaded_size / total_size) * 100)
|
||
self.progressUpdated.emit(progress)
|
||
|
||
# 5. 合并内容并解码
|
||
binary_content = b"".join(content_chunks)
|
||
|
||
if not binary_content:
|
||
self.errorOccurred.emit("下载内容为空")
|
||
return
|
||
|
||
# 6. 智能编码检测和解码
|
||
text_content = self._decode_content(binary_content)
|
||
|
||
# 7. 发射加载完成的信号
|
||
self.textLoaded.emit(text_content)
|
||
|
||
except requests.exceptions.Timeout:
|
||
self.errorOccurred.emit("请求超时,请检查网络连接或尝试重新加载")
|
||
except requests.exceptions.ConnectionError:
|
||
self.errorOccurred.emit("网络连接错误,请检查网络设置")
|
||
except requests.exceptions.RequestException as e:
|
||
self.errorOccurred.emit(f"网络请求错误: {str(e)}")
|
||
except Exception as e:
|
||
self.errorOccurred.emit(f"文本处理错误: {str(e)}")
|
||
|
||
def _decode_content(self, binary_content):
|
||
"""智能解码二进制内容"""
|
||
# 优先尝试UTF-8
|
||
encodings = ["utf-8", "gbk", "gb2312", "latin-1", "iso-8859-1", "cp1252"]
|
||
|
||
for encoding in encodings:
|
||
try:
|
||
return binary_content.decode(encoding)
|
||
except UnicodeDecodeError:
|
||
continue
|
||
|
||
# 如果所有编码都失败,使用替换错误处理
|
||
try:
|
||
return binary_content.decode("utf-8", errors="replace")
|
||
except:
|
||
# 最后尝试忽略错误
|
||
return binary_content.decode("utf-8", errors="ignore")
|
||
|
||
def cancel(self):
|
||
"""取消下载"""
|
||
if self.isRunning():
|
||
self.terminate()
|
||
self.wait(1000) # 等待线程结束
|
||
|
||
|
||
class ImageLoaderThread(QThread):
|
||
"""优化的图片加载线程"""
|
||
|
||
imageLoaded = pyqtSignal(QPixmap)
|
||
errorOccurred = pyqtSignal(str)
|
||
progressUpdated = pyqtSignal(int) # 进度更新信号
|
||
|
||
def __init__(
|
||
self, url, cache_dir="image_cache", max_cache_size=50 * 1024 * 1024
|
||
): # 50MB缓存
|
||
super().__init__()
|
||
self.url = url
|
||
self.cache_dir = cache_dir
|
||
self.max_cache_size = max_cache_size
|
||
self._setup_cache()
|
||
|
||
def _setup_cache(self):
|
||
"""设置图片缓存目录"""
|
||
if not os.path.exists(self.cache_dir):
|
||
os.makedirs(self.cache_dir)
|
||
|
||
def _get_cache_filename(self):
|
||
"""生成缓存文件名"""
|
||
parsed_url = urlparse(self.url)
|
||
filename = os.path.basename(parsed_url.path) or "image"
|
||
# 添加URL哈希避免重名
|
||
import hashlib
|
||
|
||
url_hash = hashlib.md5(self.url.encode()).hexdigest()[:8]
|
||
return f"{url_hash}_{filename}"
|
||
|
||
def _get_cached_image(self):
|
||
"""获取缓存图片"""
|
||
cache_file = os.path.join(self.cache_dir, self._get_cache_filename())
|
||
if os.path.exists(cache_file):
|
||
try:
|
||
pixmap = QPixmap(cache_file)
|
||
if not pixmap.isNull():
|
||
return pixmap
|
||
except Exception:
|
||
pass
|
||
return None
|
||
|
||
def _save_to_cache(self, pixmap):
|
||
"""保存图片到缓存"""
|
||
try:
|
||
cache_file = os.path.join(self.cache_dir, self._get_cache_filename())
|
||
pixmap.save(cache_file, "JPG", 80) # 压缩质量80%
|
||
self._cleanup_cache() # 清理过期缓存
|
||
except Exception:
|
||
pass
|
||
|
||
def _cleanup_cache(self):
|
||
"""清理过期的缓存文件"""
|
||
# noinspection PyBroadException
|
||
try:
|
||
files = []
|
||
for f in os.listdir(self.cache_dir):
|
||
filepath = os.path.join(self.cache_dir, f)
|
||
if os.path.isfile(filepath):
|
||
files.append((filepath, os.path.getmtime(filepath)))
|
||
|
||
# 按修改时间排序
|
||
files.sort(key=lambda x: x[1])
|
||
|
||
# 计算总大小
|
||
total_size = sum(os.path.getsize(f[0]) for f in files)
|
||
|
||
# 如果超过最大缓存大小,删除最旧的文件
|
||
while total_size > self.max_cache_size and files:
|
||
oldest_file = files.pop(0)
|
||
total_size -= os.path.getsize(oldest_file[0])
|
||
os.remove(oldest_file[0])
|
||
except Exception:
|
||
pass
|
||
|
||
def run(self):
|
||
"""线程执行函数"""
|
||
try:
|
||
# 1. 首先检查缓存
|
||
cached_pixmap = self._get_cached_image()
|
||
if cached_pixmap:
|
||
self.imageLoaded.emit(cached_pixmap)
|
||
return
|
||
|
||
# 2. 设置更短的超时时间
|
||
session = requests.Session()
|
||
adapter = requests.adapters.HTTPAdapter(
|
||
pool_connections=10, pool_maxsize=10, max_retries=5 # 重试2次
|
||
)
|
||
session.mount("http://", adapter)
|
||
session.mount("https://", adapter)
|
||
|
||
# 3. 流式下载,支持进度显示
|
||
response = miaoStarsBasicApi.returnSession().get(
|
||
self.url, stream=True, timeout=(20, 30) # 连接超时5秒,读取超时10秒
|
||
)
|
||
response.raise_for_status()
|
||
|
||
# 获取文件大小(如果服务器支持)
|
||
total_size = int(response.headers.get("content-length", 0))
|
||
downloaded_size = 0
|
||
|
||
# 4. 分块读取并处理
|
||
image_data = b""
|
||
for chunk in response.iter_content(chunk_size=8192):
|
||
if chunk:
|
||
image_data += chunk
|
||
downloaded_size += len(chunk)
|
||
|
||
# 更新进度(如果知道总大小)
|
||
if total_size > 0:
|
||
progress = int((downloaded_size / total_size) * 100)
|
||
self.progressUpdated.emit(progress)
|
||
|
||
# 5. 从数据创建QImage(比QPixmap更快)
|
||
image = QImage()
|
||
image.loadFromData(image_data)
|
||
|
||
if image.isNull():
|
||
raise Exception("无法加载图片数据")
|
||
|
||
# 6. 转换为QPixmap
|
||
pixmap = QPixmap.fromImage(image)
|
||
|
||
# 7. 保存到缓存
|
||
self._save_to_cache(pixmap)
|
||
|
||
# 发射加载完成的信号
|
||
self.imageLoaded.emit(pixmap)
|
||
|
||
except requests.exceptions.Timeout:
|
||
self.errorOccurred.emit("请求超时,请检查网络连接")
|
||
except requests.exceptions.ConnectionError:
|
||
self.errorOccurred.emit("网络连接错误")
|
||
except requests.exceptions.RequestException as e:
|
||
self.errorOccurred.emit(f"网络请求错误: {str(e)}")
|
||
except Exception as e:
|
||
self.errorOccurred.emit(f"图片处理错误: {str(e)}")
|