__init__.py 8.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208
  1. #!/usr/bin/env python3
  2. # -*- coding: utf-8 -*-
  3. from fastapi import APIRouter, Request, Depends,Response,HTTPException,status,Query,UploadFile,File,Form
  4. from typing import List, Optional
  5. from fastapi.responses import JSONResponse
  6. from fastapi.responses import FileResponse
  7. from sqlalchemy.orm import Session
  8. from database import get_db
  9. from config import settings
  10. from urllib import parse
  11. # from PIL import Image
  12. from models import *
  13. from utils import *
  14. from common.security import valid_access_token,valid_access_token_role
  15. import requests
  16. import hashlib
  17. import random
  18. import string
  19. import json
  20. import time
  21. import os
  22. router = APIRouter()
  23. UPLOAD_IMAGE_PATH = '/data/upload/img/'
  24. UPLOAD_mergefile_PATH = '/data/upload/mergefile/'
  25. @router.post("/upload/upload_img")
  26. async def upload_img(
  27. request: Request,
  28. file: UploadFile = File(...),
  29. db: Session = Depends(get_db), dependencies=Depends(valid_access_token_role)
  30. ):
  31. file_name = file.filename
  32. if '../' in file_name or '/' in file_name:
  33. return JSONResponse(status_code=404, content={'code': 404, "msg": '警告:禁止篡改文件路径'})
  34. # 文件后续名校验
  35. suffix = os.path.splitext(file_name)[-1]
  36. if suffix.lower() not in ['.png', '.jpg', '.jpeg']:
  37. return {
  38. 'code': 1,
  39. 'msg': '文件类型不在png,jpg范围内',
  40. }
  41. upload_path = UPLOAD_IMAGE_PATH
  42. file_dir = os.path.join(upload_path, "{}".format("poster"))
  43. if os.path.exists(file_dir) == False:
  44. os.makedirs(file_dir)
  45. file_name = new_guid() + suffix.lower()
  46. file_path = os.path.abspath("{}/{}".format(file_dir, file_name))
  47. if os.path.exists(file_path):
  48. os.remove(file_path)
  49. content = await file.read()
  50. with open(file_path, 'wb') as f:
  51. f.write(content)
  52. print('file_path:', file_path)
  53. return {
  54. 'code': 200,
  55. 'msg': '上传成功',
  56. 'data': file_name
  57. }
  58. @router.get('/get_img/get_img_by_id/{id}', response_class=FileResponse)
  59. async def get_poster_by_id(
  60. request: Request,
  61. id: str,
  62. db: Session = Depends(get_db)
  63. ):
  64. if '../' in id or '/' in id:
  65. return JSONResponse(status_code=404, content={'code': 404, "msg": '警告:禁止下载文件'})
  66. image_filepath = os.path.abspath(os.path.join(UPLOAD_IMAGE_PATH+'poster/', id )) #+ ".png"
  67. # print(image_filepath)
  68. if os.path.exists(image_filepath) == False:
  69. return {'err':'错误'}
  70. image_filepath = "./static/img/video_poster.png"
  71. # else:
  72. # file_size = os.path.getsize(image_filepath)
  73. # if file_size > (1024 * 1024 * 1.5): # 1.5M
  74. # image = Image.open(image_filepath)
  75. # image.thumbnail((675, 390))
  76. # image.save(image_filepath, 'png')
  77. return FileResponse(image_filepath)
  78. @router.post("/upload/uploadfile")
  79. async def upload_big_file( request: Request,
  80. file: UploadFile = File(...),
  81. chunknumber: str = Query(''),
  82. identifier: str = Query(''), user_id=Depends(valid_access_token)): # 分片上传文件【用唯一标志符+分片序号】作为文件名
  83. if user_id == False:
  84. return JSONResponse(status_code=404, content={'code': 404, "msg": '警告:未登录禁止上传文件'})
  85. if not file:
  86. raise HTTPException(status_code=status.HTTP_400_BAD_REQUEST, detail="文件字段缺失")
  87. if len(chunknumber) == 0 or len(identifier) == 0:
  88. return {"eroor": "没有传递相关参数"}
  89. # print(1111)
  90. if '/' in chunknumber or '/' in identifier:
  91. return JSONResponse(status_code=404, content={'code': 404, "msg": '警告:禁止篡改文件路径'})
  92. task = identifier # 获取文件唯一标识符
  93. chunk = chunknumber # 获取该分片在所有分片中的序号【客户端设定】
  94. filename = '%s%s' % (task, chunk) # 构成该分片唯一标识符
  95. file_dir = f"{UPLOAD_mergefile_PATH}/uploads/"
  96. if os.path.exists(file_dir) == False:
  97. os.makedirs(file_dir)
  98. contents = await file.read() # 异步读取文件
  99. with open(file_dir+f"{filename}", "wb") as f:
  100. f.write(contents)
  101. return {
  102. 'code': 200,
  103. 'msg': '成功',
  104. "filename": file.filename}
  105. @router.post("/upload/mergefile")
  106. async def mergefile(identifier: str = Query(''),
  107. filename: str = Query(''),
  108. chunkstar: int = Query(0), user_id=Depends(valid_access_token)): # 根据唯一标识符合并文件
  109. if user_id == False:
  110. return JSONResponse(status_code=404, content={'code': 404, "msg": '警告:未登录禁止上传文件'})
  111. if '../' in filename or '/' in filename:
  112. return JSONResponse(status_code=404, content={'code': 404, "msg": '警告:禁止篡改文件路径'})
  113. if len(filename) == 0 or len(identifier) == 0:
  114. return {"eroor": "没有传递相关参数"}
  115. suffix = os.path.splitext(filename)[-1]
  116. if suffix.lower() not in ['.png', '.jpg', '.jpeg','.doc','.docx','.xlx','.xlsx','.pdf','.mp3','.mp4']:
  117. return JSONResponse(status_code=500, content={'code': 500, "msg": '警告:文件拓展名不在白名单中'})
  118. filename = new_guid() + suffix.lower()
  119. target_filename = filename # 获取上传文件的文件名【保存的文件名】
  120. task = identifier # 获取文件的唯一标识符
  121. chunk = chunkstar # 分片序号开始的序号默认=0
  122. if os.path.isfile(f"{UPLOAD_mergefile_PATH}/uploads/{target_filename}"): # 如果客户端传递过来的文件名在服务器上已经存在,那么另外新建一个【时间戳.后缀名】文件
  123. t = time.time() # 时间戳
  124. timeUnix = str(round(t * 1000)) # 毫秒级时间戳
  125. filesuffix = os.path.splitext(target_filename)[1] # 后缀名
  126. target_filename = timeUnix + filesuffix # 新文件名【时间戳.后缀名】
  127. error_i = 0
  128. chunkfilename = ""
  129. with open(f"{UPLOAD_mergefile_PATH}/uploads/{target_filename}", 'wb') as target_file: # 创建新文件
  130. while True: # 循环把分片文件写入新建的文件
  131. if os.path.isfile(f"{UPLOAD_mergefile_PATH}/uploads/{task}{chunk}"): # 存在这个文件
  132. try:
  133. # 分片文件名
  134. chunkfilename = f"{UPLOAD_mergefile_PATH}/uploads/{task}{chunk}"
  135. # 按序打开每个分片
  136. source_file = open(chunkfilename, 'rb')
  137. # 读取分片内容写入新文件
  138. target_file.write(source_file.read())
  139. source_file.close()
  140. except IOError: # 当分片标志chunk累加到最后,文件夹里面不存在{task}{chunk}文件时,退出循环
  141. break
  142. os.remove(f"{UPLOAD_mergefile_PATH}/uploads/{task}{chunk}") # 删除分片文件
  143. else: # 【如果分片文件上传中途出错,导致中间缺少某个分片文件,跳过它,不退出循环,直到累计缺少次数大于3次,再跳出循环】
  144. error_i += 1
  145. if error_i > 3:
  146. break
  147. chunk += 1
  148. os.chmod(f"{UPLOAD_mergefile_PATH}uploads/{target_filename}", 0o664) # linux设置权限0o664、0o400
  149. return {"code": 200, "filename": f"{target_filename}"}
  150. @router.get("/download/{filename}")
  151. async def download_file(filename: str,filenameDesc: str = None, user_id=Depends(valid_access_token)):
  152. """
  153. 根据提供的文件名下载文件。
  154. :param filename: 要下载的文件的名称。
  155. """
  156. try:
  157. if user_id == False:
  158. return JSONResponse(status_code=404, content={'code': 404, "msg": '警告:未登录禁止下载文件'})
  159. # 构造文件的完整路径
  160. if '../' in filename or '/' in filename:
  161. return JSONResponse(status_code=404, content={'code': 404, "msg": '警告:禁止跨路径下载文件'})
  162. file_path = os.path.join(UPLOAD_mergefile_PATH, 'uploads/', filename)
  163. # 检查文件是否存在
  164. if not os.path.isfile(file_path):
  165. return JSONResponse(status_code=404, content={'code': 404, "msg": "文件未找到"})
  166. if not filenameDesc:
  167. filenameDesc = filename
  168. # 设置文件头部和内容类型
  169. headers = {
  170. 'Content-Disposition': f'attachment; filename={filenameDesc}'
  171. }
  172. # 使用FileResponse返回文件流
  173. return FileResponse(
  174. path=file_path,
  175. headers=headers,
  176. media_type='application/octet-stream' # 可以按需更改为适当的MIME类型
  177. )
  178. except HTTPException as e:
  179. raise e
  180. except Exception as e:
  181. # 处理其他异常情况
  182. return JSONResponse(status_code=500, content={'code': 500, "msg": '发生错误,请联系运维人员'})