AnlaAnla 1 månad sedan
incheckning
e7d1e2397c
9 ändrade filer med 161 tillägg och 0 borttagningar
  1. 0 0
      app/__init__.py
  2. 0 0
      app/api/__init__.py
  3. 61 0
      app/api/storage.py
  4. 0 0
      app/core/__init__.py
  5. 20 0
      app/core/config.py
  6. 32 0
      app/main.py
  7. 0 0
      app/utils/__init__.py
  8. 42 0
      app/utils/file_ops.py
  9. 6 0
      run_img_storage.py

+ 0 - 0
app/__init__.py


+ 0 - 0
app/api/__init__.py


+ 61 - 0
app/api/storage.py

@@ -0,0 +1,61 @@
+import os
+from fastapi import APIRouter, UploadFile, File, HTTPException
+from typing import List
+from app.core.config import settings
+from app.utils.file_ops import save_upload_file, delete_file
+
+router = APIRouter()
+
+
+@router.post("/upload", summary="上传图片 (Multipart)")
+async def upload_image(file: UploadFile = File(...)):
+    """
+    接收文件上传,保存并返回可访问的 URL。
+    树莓派调用此接口。
+    """
+    if not file.content_type.startswith("image/"):
+        raise HTTPException(status_code=400, detail="File must be an image")
+
+    try:
+        # 保存文件
+        filename = await save_upload_file(file)
+
+        # 拼接完整的 URL 地址
+        file_url = f"{settings.BASE_URL}/static/{filename}"
+
+        return {
+            "status": "success",
+            "filename": filename,
+            "url": file_url
+        }
+    except Exception as e:
+        raise HTTPException(status_code=500, detail=f"Upload failed: {str(e)}")
+
+
+# --- 赠送的实用接口 ---
+
+@router.get("/images", summary="列出所有已上传图片")
+async def list_images():
+    """查看服务器上现在存了哪些图片 (调试用)"""
+    if not os.path.exists(settings.UPLOAD_PATH):
+        return []
+
+    files = sorted(os.listdir(settings.UPLOAD_PATH), reverse=True)  # 按时间倒序(假设文件名带时间戳)
+
+    image_list = []
+    for f in files:
+        if f.lower().endswith(('.png', '.jpg', '.jpeg', '.gif')):
+            image_list.append({
+                "filename": f,
+                "url": f"{settings.BASE_URL}/static/{f}"
+            })
+    return image_list
+
+
+@router.delete("/images/{filename}", summary="删除指定图片")
+async def delete_image(filename: str):
+    """清理图片"""
+    success = delete_file(filename)
+    if not success:
+        raise HTTPException(status_code=404, detail="File not found")
+    return {"status": "deleted", "filename": filename}

+ 0 - 0
app/core/__init__.py


+ 20 - 0
app/core/config.py

@@ -0,0 +1,20 @@
+import os
+
+
+class Settings:
+    # 服务器配置
+    HOST: str = "192.168.77.249"
+    PORT: int = 7733
+
+    # 基础 URL (返回给树莓派用于访问)
+    BASE_URL: str = f"http://{HOST}:{PORT}"
+
+    # 图片存储文件夹名称
+    UPLOAD_DIR_NAME: str = "uploads"
+
+    # 绝对路径
+    BASE_DIR: str = os.path.dirname(os.path.dirname(os.path.dirname(os.path.abspath(__file__))))
+    UPLOAD_PATH: str = os.path.join(BASE_DIR, UPLOAD_DIR_NAME)
+
+
+settings = Settings()

+ 32 - 0
app/main.py

@@ -0,0 +1,32 @@
+from fastapi import FastAPI
+from fastapi.staticfiles import StaticFiles
+from fastapi.middleware.cors import CORSMiddleware
+from app.api import storage
+from app.core.config import settings
+import os
+
+# 确保启动时创建目录
+if not os.path.exists(settings.UPLOAD_PATH):
+    os.makedirs(settings.UPLOAD_PATH)
+
+app = FastAPI(title="Simple Image Server", version="1.0.0")
+
+# 1. 挂载静态目录
+# 这样访问 http://192.168.77.249:7733/static/xxx.jpg 就能直接看到图片
+app.mount("/static", StaticFiles(directory=settings.UPLOAD_PATH), name="static")
+
+# 2. 设置 CORS (允许所有来源,方便调试)
+app.add_middleware(
+    CORSMiddleware,
+    allow_origins=["*"],
+    allow_credentials=True,
+    allow_methods=["*"],
+    allow_headers=["*"],
+)
+
+# 3. 注册路由
+app.include_router(storage.router, prefix="/api")
+
+@app.get("/")
+async def root():
+    return {"message": "Image Server is running", "docs": f"{settings.BASE_URL}/docs"}

+ 0 - 0
app/utils/__init__.py


+ 42 - 0
app/utils/file_ops.py

@@ -0,0 +1,42 @@
+import os
+import time
+import uuid
+import aiofiles
+from fastapi import UploadFile
+from app.core.config import settings
+
+
+async def save_upload_file(file: UploadFile) -> str:
+    """
+    保存上传的文件,返回文件名。
+    文件名格式: timestamp_uuid.ext
+    """
+    # 1. 确保目录存在
+    if not os.path.exists(settings.UPLOAD_PATH):
+        os.makedirs(settings.UPLOAD_PATH)
+
+    # 2. 生成唯一文件名
+    # 获取扩展名 (如 .jpg)
+    filename = file.filename
+    ext = os.path.splitext(filename)[1] if filename else ".jpg"
+
+    # 使用 时间戳 + UUID 防止重名
+    unique_name = f"{int(time.time())}_{uuid.uuid4().hex[:8]}{ext}"
+    file_path = os.path.join(settings.UPLOAD_PATH, unique_name)
+
+    # 3. 异步写入磁盘
+    async with aiofiles.open(file_path, 'wb') as out_file:
+        # 即使是 UploadFile,读取 content 也是异步流
+        content = await file.read()
+        await out_file.write(content)
+
+    return unique_name
+
+
+def delete_file(filename: str) -> bool:
+    """删除指定文件"""
+    file_path = os.path.join(settings.UPLOAD_PATH, filename)
+    if os.path.exists(file_path):
+        os.remove(file_path)
+        return True
+    return False

+ 6 - 0
run_img_storage.py

@@ -0,0 +1,6 @@
+import uvicorn
+from app.core.config import settings
+
+if __name__ == '__main__':
+    print(f"Starting Image Server on {settings.HOST}:{settings.PORT}...")
+    uvicorn.run("app.main:app", host="0.0.0.0", port=settings.PORT, reload=True)