# -*- coding: utf-8 -*- import hashlib import logging import os import re import aiofiles from sqlalchemy import text from website import db_mysql from website import errors from website import settings from website.handler import APIHandler, authenticated class UploadHandler(APIHandler): @authenticated async def post(self): file_metas = self.request.files.get('file', None) if not file_metas: raise errors.HTTPAPIError(errors.ERROR_BAD_REQUEST, "请选择文件") filename = file_metas[0].filename punctuation = """!"#$%&'()*+,/:;<=>?@[\]^`{|}~ """ regex = re.compile('[%s]' % re.escape(punctuation)) filename = regex.sub("", filename.replace('..', '')) file_size = len(file_metas[0].body) logging.info("file_size: %s", file_size) if file_size > 300 * 1024 * 1024: raise errors.HTTPAPIError(errors.ERROR_BAD_REQUEST, 'Exceed 300M size limit') filetype = filename.split(".") and filename.split(".")[-1] or "" file_upload_dir = settings.file_upload_dir os.makedirs(file_upload_dir, exist_ok=True) md5_str = hashlib.md5(file_metas[0].body).hexdigest() row = None with self.app_mysql.connect() as conn: sql = text("select id from files where md5_str=:md5_str") cur = conn.execute(sql, {"md5_str": md5_str}) row = cur.fetchone() if not row: filepath = os.path.join(settings.file_upload_dir, md5_str + '_' + filename) if not os.path.exists(filepath): for meta in file_metas: # filename = meta['filename'] async with aiofiles.open(filepath, 'wb') as f: await f.write(meta['body']) sql_insert = text( """insert into files(filename, filepath, md5_str, filesize, filetype, user) values(:filename, :filepath, :md5_str, :file_size, :filetype, :user)""" ) conn.execute(sql_insert, {"filename": filename, "filepath": filepath, "md5_str": md5_str, "file_size": int(file_size / 1024 / 1024), "filetype": filetype, "user": self.current_user.id}) conn.commit() self.finish({"result": md5_str}) class DeleteHandler(APIHandler): @authenticated def post(self): md5_str = self.get_escaped_argument("file_md5", "") if not md5_str: raise errors.HTTPAPIError(errors.ERROR_BAD_REQUEST, "file md5 is required") logging.info("md5_str: %s", md5_str) row = None with self.app_mysql.connect() as conn: sql = text("select filepath from files where md5_str=:md5_str") cur = conn.execute(sql, {"md5_str": md5_str}) row = db_mysql.to_json(cur) if not row: raise errors.HTTPAPIError(errors.ERROR_BAD_REQUEST, "file not found") filepath = row["filepath"] if os.path.exists(filepath): os.remove(filepath) sql_del = text("delete from files where md5_str=:md5_str") conn.execute(sql_del, {"md5_str": md5_str}) conn.commit() self.finish() class BigFileUploadHandler(APIHandler): async def post(self): file_metas = self.request.files.get('file', None) if not file_metas: raise errors.HTTPAPIError(errors.ERROR_BAD_REQUEST, "请选择文件") filename = file_metas[0].filename punctuation = """!"#$%&'()*+,/:;<=>?@[\]^`{|}~ """ regex = re.compile('[%s]' % re.escape(punctuation)) filename = regex.sub("", filename.replace('..', '')) file_size = len(file_metas[0].body) logging.info("file_size: %s", file_size) if file_size > 300 * 1024 * 1024: raise errors.HTTPAPIError(errors.ERROR_BAD_REQUEST, 'Exceed 300M size limit') filetype = filename.split(".") and filename.split(".")[-1] or "" file_upload_dir = settings.file_upload_dir os.makedirs(file_upload_dir, exist_ok=True) # md5_str = hashlib.md5(file_metas[0].body).hexdigest() filepath = os.path.join(settings.file_upload_dir, filename) if not os.path.exists(filepath): for meta in file_metas: # filename = meta['filename'] # with open(filepath, 'wb') as f: # f.write(meta['body']) async with aiofiles.open(filepath, 'wb') as f: await f.write(meta['body']) self.finish()