duqing2026's picture
fix: 解决Jinja与Vue冲突;新增健康检查与发布指南
19abb06
import os
import io
import zipfile
from flask import Flask, render_template, request, send_file, jsonify
from werkzeug.exceptions import RequestEntityTooLarge
from PIL import Image, ImageOps
app = Flask(__name__)
app.config['MAX_CONTENT_LENGTH'] = 16 * 1024 * 1024 # 16MB max upload
@app.route('/')
def index():
return render_template('index.html')
@app.route('/api/health')
def health():
return jsonify({'status': 'ok'}), 200
@app.route('/api/split', methods=['POST'])
def split_image():
if 'file' not in request.files:
return jsonify({'error': 'No file uploaded'}), 400
file = request.files['file']
if file.filename == '':
return jsonify({'error': 'No file selected'}), 400
try:
# Get parameters
rows = int(request.form.get('rows', 3))
cols = int(request.form.get('cols', 3))
out_format = (request.form.get('format') or '').upper()
quality = int(request.form.get('quality', 90))
square = request.form.get('square', 'false') == 'true'
strict_equal = request.form.get('strict_equal', 'false') == 'true'
# Load image
img = Image.open(file.stream)
# Auto-orient by EXIF if present
try:
img = ImageOps.exif_transpose(img)
except Exception:
pass
# Convert to RGB if necessary (e.g. for RGBA/P images saving as JPEG)
# But we'll try to keep original format if possible, or default to PNG/JPG
format = img.format or 'PNG'
if out_format in ['JPEG', 'PNG', 'WEBP']:
format = out_format
if format not in ['JPEG', 'PNG', 'WEBP']:
format = 'PNG'
# Calculate dimensions
width, height = img.size
# Optional square crop (center)
if square:
side = min(width, height)
left = (width - side) // 2
top = (height - side) // 2
img = img.crop((left, top, left + side, top + side))
width, height = img.size
# Compute base tile size
tile_width = width // cols
tile_height = height // rows
if strict_equal:
# Crop to exact multiple to make all tiles equal
exact_w = tile_width * cols
exact_h = tile_height * rows
left = (width - exact_w) // 2
top = (height - exact_h) // 2
img = img.crop((left, top, left + exact_w, top + exact_h))
width, height = img.size
tile_width = width // cols
tile_height = height // rows
# Create ZIP in memory
memory_file = io.BytesIO()
with zipfile.ZipFile(memory_file, 'w', zipfile.ZIP_DEFLATED) as zf:
for r in range(rows):
for c in range(cols):
# Crop
left = c * tile_width
upper = r * tile_height
right = left + tile_width
lower = upper + tile_height
# Handle last row/col to include remainder pixels when not strict_equal
if not strict_equal:
if c == cols - 1:
right = width
if r == rows - 1:
lower = height
tile = img.crop((left, upper, right, lower))
# Save tile to bytes
tile_bytes = io.BytesIO()
save_kwargs = {}
if format == 'JPEG':
if tile.mode in ('RGBA', 'P'):
tile = tile.convert('RGB')
save_kwargs['quality'] = max(1, min(quality, 95))
save_kwargs['optimize'] = True
save_kwargs['progressive'] = True
elif format == 'WEBP':
save_kwargs['quality'] = max(1, min(quality, 95))
tile.save(tile_bytes, format=format, **save_kwargs)
tile_bytes.seek(0)
# Add to zip (1-based index)
# Naming convention: {original_name}_{index}.{ext}
# Grid order: 1, 2, 3...
index = r * cols + c + 1
filename = f"tile_{index:02d}.{format.lower()}"
zf.writestr(filename, tile_bytes.read())
# Add posting order guide
order_lines = []
order_lines.append(f"发布顺序指南({rows}x{cols})")
order_lines.append("从左到右,从上到下:")
nums = [f"{i:02d}" for i in range(1, rows * cols + 1)]
for r in range(rows):
row_slice = nums[r * cols:(r + 1) * cols]
order_lines.append(" " + " ".join(row_slice))
order_lines.append("")
order_lines.append("建议:")
order_lines.append("1. 朋友圈/小红书九宫格:按上述顺序依次选择图片。")
order_lines.append("2. 若平台有排序规则,请关闭自动排序或手动调整。")
zf.writestr("posting_order.txt", "\n".join(order_lines))
memory_file.seek(0)
return send_file(
memory_file,
mimetype='application/zip',
as_attachment=True,
download_name=f'grid_split_{rows}x{cols}.zip'
)
except Exception as e:
return jsonify({'error': str(e)}), 500
@app.errorhandler(RequestEntityTooLarge)
def handle_file_too_large(e):
return jsonify({'error': '文件过大,超过限制(最大16MB)'}), 413
if __name__ == '__main__':
app.run(host='0.0.0.0', port=7860)