excel2pic/app.py

65 lines
2.3 KiB
Python

from fastapi import FastAPI, UploadFile, File, Form, HTTPException
from fastapi.responses import StreamingResponse, JSONResponse
from core.renderer import ExcelRenderer
import io
import logging
# Configure logging
logging.basicConfig(level=logging.INFO)
logger = logging.getLogger(__name__)
app = FastAPI(
title="Excel2Pic API",
description="A lightweight service to convert Excel sheets to images.",
version="0.1.0"
)
@app.post("/api/v1/convert", summary="Convert Excel to Image")
async def convert_excel(
file: UploadFile = File(..., description="The Excel file to convert"),
sheet_name: str = Form(None, description="Name of the sheet to convert (optional, defaults to active sheet)"),
scale: int = Form(3, description="Scaling factor for high-DPI rendering (default 3, best for mobile/retina screens)"),
):
"""
Convert an uploaded Excel file to a PNG image.
"""
# Validation
if not file.filename.endswith(('.xlsx', '.xls')):
raise HTTPException(status_code=400, detail="Invalid file format. Please upload .xlsx or .xls file.")
try:
# Read file content
contents = await file.read()
# Initialize Renderer
# Note: In a real deployment, font paths might come from env vars
renderer = ExcelRenderer(contents)
# Render
# Use scale parameter
image_bytes = renderer.render_to_bytes(sheet_name=sheet_name, scale=scale)
# Return as streaming response
# Handle Chinese filenames in Content-Disposition
from urllib.parse import quote
filename = file.filename.split('.')[0] + ".png"
encoded_filename = quote(filename)
return StreamingResponse(
io.BytesIO(image_bytes),
media_type="image/png",
headers={"Content-Disposition": f"inline; filename*=utf-8''{encoded_filename}"}
)
except ValueError as ve:
# Often raised when sheet name is not found
logger.warning(f"Value Error: {str(ve)}")
raise HTTPException(status_code=400, detail=str(ve))
except Exception as e:
logger.error(f"Internal Server Error: {str(e)}", exc_info=True)
raise HTTPException(status_code=500, detail=f"An error occurred during conversion: {str(e)}")
@app.get("/health", summary="Health Check")
def health_check():
return {"status": "ok"}