调整验证码图片大小

This commit is contained in:
2026-06-12 20:18:23 +08:00
parent 131f06c720
commit bac4a507aa
4 changed files with 56 additions and 15 deletions

View File

@@ -1,8 +1,8 @@
FROM python:3.10-slim FROM python:3.10-slim
# 系统依赖python-magic 需要 libmagic # 系统依赖python-magic 需要 libmagic,验证码需要清晰字体
RUN apt-get update && \ RUN apt-get update && \
apt-get install -y --no-install-recommends libmagic1 && \ apt-get install -y --no-install-recommends libmagic1 fonts-dejavu-core && \
rm -rf /var/lib/apt/lists/* rm -rf /var/lib/apt/lists/*
WORKDIR /app WORKDIR /app

View File

@@ -38,6 +38,16 @@ NAME_REGEX = re.compile(r'^[\u4e00-\u9fa5a-zA-Z0-9_]{2,20}$')
# 文件名中不安全的字符 # 文件名中不安全的字符
UNSAFE_FILENAME_CHARS = re.compile(r'[<>:"/\\|?*\x00-\x1f]') UNSAFE_FILENAME_CHARS = re.compile(r'[<>:"/\\|?*\x00-\x1f]')
# 验证码图片参数
CAPTCHA_IMAGE_SIZE = (320, 100)
CAPTCHA_FONT_SIZE = 68
CAPTCHA_FONT_PATHS = (
"/usr/share/fonts/truetype/dejavu/DejaVuSans-Bold.ttf",
"/usr/local/share/fonts/DejaVuSans-Bold.ttf",
"/System/Library/Fonts/Supplemental/Arial Bold.ttf",
"/System/Library/Fonts/Supplemental/Verdana Bold.ttf",
)
# ── 工具函数 ──────────────────────────────────────────────────────── # ── 工具函数 ────────────────────────────────────────────────────────
def sanitize_name(name: str) -> str | None: def sanitize_name(name: str) -> str | None:
@@ -88,9 +98,25 @@ def generate_captcha_text(length: int = 4) -> str:
return ''.join(random.choices(chars, k=length)) return ''.join(random.choices(chars, k=length))
def load_captcha_font(size: int = CAPTCHA_FONT_SIZE) -> ImageFont.ImageFont:
"""加载适合验证码的大号字体,兼容 Linux/Docker 和 macOS 本地环境"""
for font_path in CAPTCHA_FONT_PATHS:
if not os.path.exists(font_path):
continue
try:
return ImageFont.truetype(font_path, size)
except OSError:
continue
try:
return ImageFont.truetype("DejaVuSans-Bold.ttf", size)
except OSError:
return ImageFont.load_default(size=size)
def generate_captcha_image(text: str) -> Image.Image: def generate_captcha_image(text: str) -> Image.Image:
"""生成验证码图片(含干扰线和噪点)""" """生成验证码图片(含干扰线和噪点)"""
width, height = 320, 100 width, height = CAPTCHA_IMAGE_SIZE
# 浅色随机背景 # 浅色随机背景
bg_color = (random.randint(235, 255), random.randint(235, 255), random.randint(235, 255)) bg_color = (random.randint(235, 255), random.randint(235, 255), random.randint(235, 255))
image = Image.new('RGB', (width, height), bg_color) image = Image.new('RGB', (width, height), bg_color)
@@ -109,15 +135,15 @@ def generate_captcha_image(text: str) -> Image.Image:
dot_color = (random.randint(140, 200), random.randint(140, 200), random.randint(140, 200)) dot_color = (random.randint(140, 200), random.randint(140, 200), random.randint(140, 200))
draw.point((x, y), fill=dot_color) draw.point((x, y), fill=dot_color)
# 绘制文字 font = load_captcha_font()
try: char_slot_width = width // len(text)
font = ImageFont.truetype("/usr/share/fonts/truetype/dejavu/DejaVuSans-Bold.ttf", 65)
except OSError:
font = ImageFont.load_default()
for i, char in enumerate(text): for i, char in enumerate(text):
x = 20 + i * 72 bbox = draw.textbbox((0, 0), char, font=font)
y = random.randint(6, 18) char_width = bbox[2] - bbox[0]
char_height = bbox[3] - bbox[1]
x = i * char_slot_width + (char_slot_width - char_width) // 2 - bbox[0] + random.randint(-4, 4)
y = (height - char_height) // 2 - bbox[1] + random.randint(-5, 5)
color = (random.randint(0, 80), random.randint(0, 80), random.randint(0, 80)) color = (random.randint(0, 80), random.randint(0, 80), random.randint(0, 80))
draw.text((x, y), char, font=font, fill=color) draw.text((x, y), char, font=font, fill=color)

View File

@@ -610,8 +610,11 @@ input[type="text"].input-error {
} }
.modal-captcha-img { .modal-captcha-img {
height: 46px; height: 86px;
flex: 1; width: min(260px, 100%);
max-width: 260px;
min-width: 0;
flex: 1 1 0;
border-radius: var(--radius-sm); border-radius: var(--radius-sm);
border: 1.5px solid var(--border); border: 1.5px solid var(--border);
cursor: pointer; cursor: pointer;
@@ -721,6 +724,11 @@ input[type="text"].input-error {
height: 38px; height: 38px;
} }
.modal-captcha-img {
height: 72px;
max-width: 100%;
}
.form-actions { .form-actions {
flex-direction: column; flex-direction: column;
} }

View File

@@ -610,9 +610,11 @@ input[type="text"].input-error {
} }
.modal-captcha-img { .modal-captcha-img {
height: 80px; height: 86px;
width: 240px; width: min(260px, 100%);
flex: none; max-width: 260px;
min-width: 0;
flex: 1 1 0;
border-radius: var(--radius-sm); border-radius: var(--radius-sm);
border: 1.5px solid var(--border); border: 1.5px solid var(--border);
cursor: pointer; cursor: pointer;
@@ -722,6 +724,11 @@ input[type="text"].input-error {
height: 38px; height: 38px;
} }
.modal-captcha-img {
height: 72px;
max-width: 100%;
}
.form-actions { .form-actions {
flex-direction: column; flex-direction: column;
} }