import datetime, shutil, time #from datetime import datetime, time, timedelta from fastapi import APIRouter, Body, Depends, File, Form, Header, HTTPException, Query, Response, status, UploadFile from pydantic import BaseModel, EmailStr, Field from typing import Dict, List, Optional, Set, Union from app.lib_general import * from ..log import * from app.config import settings from app.db_sql import * from .api_crud import delete_obj_template, get_obj_template, get_obj_li_template, patch_obj_template, post_obj_template from app.models.hosted_file_models import Hosted_File_Base from app.models.response_models import * router = APIRouter() # This just needs to return the currect model for a hosted_file # Everything else seems to be workign well # Should this also do something with meta data and updating the DB? @router.post('/upload_files/') async def create_upload_files( files: List[UploadFile] = File(...), account_id: str = Form(..., min_length=1, max_length=22), filename: Optional[str] = Form(...), object_type: Optional[str] = Form(...), object_id: Optional[str] = Form(..., min_length=1, max_length=22), x_account_id: Optional[str] = Header(..., ), return_obj: Optional[bool] = True, by_alias: Optional[bool] = True, exclude_unset: Optional[bool] = True, ): data = {} data['account_id'] = account_id #data['filename'] = filename data['object_type'] = object_type data['object_id'] = object_id data['file_li'] = [] log.debug(await save_file_li(files)) response_li = [] for file in files: file_info = {} file_info['filename'] = file.filename file_info['content_type'] = file.content_type file.file.seek(0, os.SEEK_END) file_size = file.file.tell() file.file.seek(0) # The file will not properly save if seek is not reset to 0. file_info['size'] = file_size log.debug(file_size) data['file_li'].append(file_info) return mk_resp(data=data) #response = { 'response_data': response_data, 'response_li':response_li } #response['filenames'] = [file.filename for file in files] #return response #return {'filenames': [file.filename for file in files]} # This is not needed. Just for reference of the mk_resp() function @router.post('/', response_model=Resp_Body_Base) async def post_upload_file( account_id: str = Query(..., min_length=1, max_length=22), ): if return_obj: if order_cart_obj := load_order_cart_obj(order_cart_id=order_cart_id, inc_order_cart_line_li=inc_order_cart_line_li, inc_order_cart_cfg=inc_order_cart_cfg): data = order_cart_obj.dict(by_alias=True, exclude_unset=False) return mk_resp(data=data) else: return mk_resp(data=False, status_code=404) # Not Found else: return mk_resp(data=True) async def save_file_li(file_li: List[UploadFile] = File(...)): log.setLevel(logging.DEBUG) # DEBUG, INFO, WARNING, ERROR, EXCEPTION, CRITICAL log.debug(locals()) hosted_file_path = '/home/scott/tmp/hosted_file_dev/' log.debug(shutil.disk_usage(hosted_file_path)) result = [] for file in file_li: log.debug(f'{file.filename}') file.file.seek(0, os.SEEK_END) file_size = file.file.tell() file.file.seek(0) # The file will not properly save if seek is not reset to 0. #file_info['size'] = request_file_size log.debug(file_size) file_hash = await get_file_object_hash(file.file) log.debug(file_hash) # 16384 bytes is the default # 4096 8192 16384 32768 65536 131072 262144 524288 1048576 bytes buffer_size = 524288 #file_src = file.filename #f_src = open(file_src, 'rb') f_src = file.file # Don't need to do open(file_src, 'rb') since it is already "open" #file_dest = f'{hosted_file_path}{file.filename}' file_dest = f'{hosted_file_path}{file_hash}.file' f_dest = open(file_dest, 'wb') timer_start = time.process_time() shutil.copyfileobj(f_src, f_dest, buffer_size) timer_end = time.process_time() elapsed_time = timer_end - timer_start log.debug(f'Elapsed time: {elapsed_time}') result.append({ 'filename': file.filename }) log.debug(shutil.disk_usage(hosted_file_path)) return result async def get_file_object_hash(file_object): #log.setLevel(logging.WARNING) # DEBUG, INFO, WARNING, ERROR, EXCEPTION, CRITICAL log.debug(locals()) # 4096 bytes is the current block size on my workstation and Linode server # 4096 8192 16384 32768 65536 131072 262144 524288 1048576 bytes block_size = 131072 hash_value = hashlib.sha256() timer_start = time.process_time() for chunk in iter(lambda: file_object.read(block_size), b""): hash_value.update(chunk) file_hash = hash_value.hexdigest() file_object.seek(0) # The file will not properly save if seek is not reset to 0. timer_end = time.process_time() elapsed_time = timer_end - timer_start #log.debug(f'Elapsed time: {elapsed_time}') return file_hash # def allowed_file_extension(filename): # return False # return '.' in filename and filename.rsplit('.', 1)[1].lower() in app.config['ALLOWED_EXTENSIONS'] # def guess_file_extension(filename): # return filename.rsplit('.', 1)[1].lower() # def copyLargeFile(src, dest, buffer_size=16000): # with open(src, 'rb') as fsrc: # with open(dest, 'wb') as fdest: # shutil.copyfileobj(fsrc, fdest, buffer_size)