#!/usr/bin/env python3 # -*- coding: utf-8 -*- from fastapi import APIRouter, Request, Depends,Response,HTTPException,status,Query,UploadFile,File,Form from typing import List, Optional from fastapi.responses import JSONResponse from fastapi.responses import FileResponse from sqlalchemy.orm import Session from database import get_db from config import settings from urllib import parse # from PIL import Image from models import * from utils import * from common.security import valid_access_token,valid_access_token_role import requests import hashlib import random import string import json import time import os router = APIRouter() UPLOAD_IMAGE_PATH = '/data/upload/img/' UPLOAD_mergefile_PATH = '/data/upload/mergefile/' @router.post("/upload/upload_img") async def upload_img( request: Request, file: UploadFile = File(...), db: Session = Depends(get_db), dependencies=Depends(valid_access_token_role) ): file_name = file.filename if '../' in file_name or '/' in file_name: return JSONResponse(status_code=404, content={'code': 404, "msg": '警告:禁止篡改文件路径'}) # 文件后续名校验 suffix = os.path.splitext(file_name)[-1] if suffix.lower() not in ['.png', '.jpg', '.jpeg']: return { 'code': 1, 'msg': '文件类型不在png,jpg范围内', } upload_path = UPLOAD_IMAGE_PATH file_dir = os.path.join(upload_path, "{}".format("poster")) if os.path.exists(file_dir) == False: os.makedirs(file_dir) file_name = new_guid() + suffix.lower() file_path = os.path.abspath("{}/{}".format(file_dir, file_name)) if os.path.exists(file_path): os.remove(file_path) content = await file.read() with open(file_path, 'wb') as f: f.write(content) print('file_path:', file_path) return { 'code': 200, 'msg': '上传成功', 'data': file_name } @router.get('/get_img/get_img_by_id/{id}', response_class=FileResponse) async def get_poster_by_id( request: Request, id: str, db: Session = Depends(get_db) ): if '../' in id or '/' in id: return JSONResponse(status_code=404, content={'code': 404, "msg": '警告:禁止下载文件'}) image_filepath = os.path.abspath(os.path.join(UPLOAD_IMAGE_PATH+'poster/', id )) #+ ".png" # print(image_filepath) if os.path.exists(image_filepath) == False: return {'err':'错误'} image_filepath = "./static/img/video_poster.png" # else: # file_size = os.path.getsize(image_filepath) # if file_size > (1024 * 1024 * 1.5): # 1.5M # image = Image.open(image_filepath) # image.thumbnail((675, 390)) # image.save(image_filepath, 'png') return FileResponse(image_filepath) @router.post("/upload/uploadfile") async def upload_big_file( request: Request, file: UploadFile = File(...), chunknumber: str = Query(''), identifier: str = Query(''), user_id=Depends(valid_access_token)): # 分片上传文件【用唯一标志符+分片序号】作为文件名 if user_id == False: return JSONResponse(status_code=404, content={'code': 404, "msg": '警告:未登录禁止上传文件'}) if not file: raise HTTPException(status_code=status.HTTP_400_BAD_REQUEST, detail="文件字段缺失") if len(chunknumber) == 0 or len(identifier) == 0: return {"eroor": "没有传递相关参数"} # print(1111) if '/' in chunknumber or '/' in identifier: return JSONResponse(status_code=404, content={'code': 404, "msg": '警告:禁止篡改文件路径'}) task = identifier # 获取文件唯一标识符 chunk = chunknumber # 获取该分片在所有分片中的序号【客户端设定】 filename = '%s%s' % (task, chunk) # 构成该分片唯一标识符 file_dir = f"{UPLOAD_mergefile_PATH}/uploads/" if os.path.exists(file_dir) == False: os.makedirs(file_dir) contents = await file.read() # 异步读取文件 with open(file_dir+f"{filename}", "wb") as f: f.write(contents) return { 'code': 200, 'msg': '成功', "filename": file.filename} @router.post("/upload/mergefile") async def mergefile(identifier: str = Query(''), filename: str = Query(''), chunkstar: int = Query(0), user_id=Depends(valid_access_token)): # 根据唯一标识符合并文件 if user_id == False: return JSONResponse(status_code=404, content={'code': 404, "msg": '警告:未登录禁止上传文件'}) if '../' in filename or '/' in filename: return JSONResponse(status_code=404, content={'code': 404, "msg": '警告:禁止篡改文件路径'}) if len(filename) == 0 or len(identifier) == 0: return {"eroor": "没有传递相关参数"} suffix = os.path.splitext(filename)[-1] if suffix.lower() not in ['.png', '.jpg', '.jpeg','.doc','.docx','.xlx','.xlsx','.pdf','.mp3','.mp4']: return JSONResponse(status_code=500, content={'code': 500, "msg": '警告:文件拓展名不在白名单中'}) filename = new_guid() + suffix.lower() target_filename = filename # 获取上传文件的文件名【保存的文件名】 task = identifier # 获取文件的唯一标识符 chunk = chunkstar # 分片序号开始的序号默认=0 if os.path.isfile(f"{UPLOAD_mergefile_PATH}/uploads/{target_filename}"): # 如果客户端传递过来的文件名在服务器上已经存在,那么另外新建一个【时间戳.后缀名】文件 t = time.time() # 时间戳 timeUnix = str(round(t * 1000)) # 毫秒级时间戳 filesuffix = os.path.splitext(target_filename)[1] # 后缀名 target_filename = timeUnix + filesuffix # 新文件名【时间戳.后缀名】 error_i = 0 chunkfilename = "" with open(f"{UPLOAD_mergefile_PATH}/uploads/{target_filename}", 'wb') as target_file: # 创建新文件 while True: # 循环把分片文件写入新建的文件 if os.path.isfile(f"{UPLOAD_mergefile_PATH}/uploads/{task}{chunk}"): # 存在这个文件 try: # 分片文件名 chunkfilename = f"{UPLOAD_mergefile_PATH}/uploads/{task}{chunk}" # 按序打开每个分片 source_file = open(chunkfilename, 'rb') # 读取分片内容写入新文件 target_file.write(source_file.read()) source_file.close() except IOError: # 当分片标志chunk累加到最后,文件夹里面不存在{task}{chunk}文件时,退出循环 break os.remove(f"{UPLOAD_mergefile_PATH}/uploads/{task}{chunk}") # 删除分片文件 else: # 【如果分片文件上传中途出错,导致中间缺少某个分片文件,跳过它,不退出循环,直到累计缺少次数大于3次,再跳出循环】 error_i += 1 if error_i > 3: break chunk += 1 os.chmod(f"{UPLOAD_mergefile_PATH}uploads/{target_filename}", 0o664) # linux设置权限0o664、0o400 return {"code": 200, "filename": f"{target_filename}"} @router.get("/download/{filename}") async def download_file(filename: str,filenameDesc: str = None, user_id=Depends(valid_access_token)): """ 根据提供的文件名下载文件。 :param filename: 要下载的文件的名称。 """ try: if user_id == False: return JSONResponse(status_code=404, content={'code': 404, "msg": '警告:未登录禁止下载文件'}) # 构造文件的完整路径 if '../' in filename or '/' in filename: return JSONResponse(status_code=404, content={'code': 404, "msg": '警告:禁止跨路径下载文件'}) file_path = os.path.join(UPLOAD_mergefile_PATH, 'uploads/', filename) # 检查文件是否存在 if not os.path.isfile(file_path): return JSONResponse(status_code=404, content={'code': 404, "msg": "文件未找到"}) if not filenameDesc: filenameDesc = filename # 设置文件头部和内容类型 headers = { 'Content-Disposition': f'attachment; filename={filenameDesc}' } # 使用FileResponse返回文件流 return FileResponse( path=file_path, headers=headers, media_type='application/octet-stream' # 可以按需更改为适当的MIME类型 ) except HTTPException as e: raise e except Exception as e: # 处理其他异常情况 return JSONResponse(status_code=500, content={'code': 500, "msg": '发生错误,请联系运维人员'})