Skip to content

Commit 4e06948

Browse files
committed
新增存储引擎统一文件读写
1 parent 8935da8 commit 4e06948

File tree

3 files changed

+58
-26
lines changed

3 files changed

+58
-26
lines changed

main.py

Lines changed: 11 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,5 @@
11
import datetime
2-
import os
32
import uuid
4-
import threading
53
import random
64
import asyncio
75
from pathlib import Path
@@ -16,6 +14,7 @@
1614

1715
import settings
1816
from database import get_session, Codes, init_models, engine
17+
from storage import STORAGE_ENGINE
1918

2019
app = FastAPI(debug=settings.DEBUG)
2120

@@ -26,6 +25,8 @@
2625
STATIC_URL = settings.STATIC_URL
2726
app.mount(STATIC_URL, StaticFiles(directory=DATA_ROOT), name="static")
2827

28+
storage = STORAGE_ENGINE[settings.STORAGE_ENGINE]()
29+
2930

3031
@app.on_event('startup')
3132
async def startup():
@@ -45,18 +46,13 @@ async def startup():
4546
error_ip_count = {}
4647

4748

48-
def delete_file(files):
49-
for file in files:
50-
if file['type'] != 'text':
51-
os.remove(DATA_ROOT / file['text'].lstrip(STATIC_URL + '/'))
52-
53-
5449
async def delete_expire_files():
5550
while True:
5651
async with AsyncSession(engine, expire_on_commit=False) as s:
5752
query = select(Codes).where(or_(Codes.exp_time < datetime.datetime.now(), Codes.count == 0))
5853
exps = (await s.execute(query)).scalars().all()
59-
await asyncio.to_thread(delete_file, [{'type': old.type, 'text': old.text} for old in exps])
54+
files = [{'type': old.type, 'text': old.text} for old in exps]
55+
await storage.delete_files(files)
6056
exps_ids = [exp.id for exp in exps]
6157
query = delete(Codes).where(Codes.id.in_(exps_ids))
6258
await s.execute(query)
@@ -71,18 +67,6 @@ async def get_code(s: AsyncSession):
7167
return str(code)
7268

7369

74-
def get_file_name(key, ext, file, file_bytes):
75-
now = datetime.datetime.now()
76-
path = DATA_ROOT / f"upload/{now.year}/{now.month}/{now.day}/"
77-
name = f'{key}.{ext}'
78-
if not path.exists():
79-
path.mkdir(parents=True)
80-
filepath = path / name
81-
with open(filepath, 'wb') as f:
82-
f.write(file_bytes)
83-
return f"{STATIC_URL}/{filepath.relative_to(DATA_ROOT)}", file.content_type, file.filename
84-
85-
8670
@app.get(f'/{settings.ADMIN_ADDRESS}')
8771
async def admin():
8872
return HTMLResponse(admin_html)
@@ -103,7 +87,7 @@ async def admin_delete(request: Request, code: str, s: AsyncSession = Depends(ge
10387
if request.headers.get('pwd') == settings.ADMIN_PASSWORD:
10488
query = select(Codes).where(Codes.code == code)
10589
file = (await s.execute(query)).scalars().first()
106-
await asyncio.to_thread(delete_file, [{'type': file.type, 'text': file.text}])
90+
await storage.delete_file({'type': file.type, 'text': file.text})
10791
await s.delete(file)
10892
await s.commit()
10993
return {'code': 200, 'msg': '删除成功'}
@@ -142,7 +126,8 @@ async def get_file(code: str, s: AsyncSession = Depends(get_session)):
142126
if info.type == 'text':
143127
return {'code': code, 'msg': '查询成功', 'data': info.text}
144128
else:
145-
return FileResponse(DATA_ROOT / info.text.lstrip(STATIC_URL + '/'), filename=info.name)
129+
filepath = await storage.get_filepath(info.text)
130+
return FileResponse(filepath, filename=info.name)
146131
else:
147132
return {'code': 404, 'msg': '口令不存在'}
148133

@@ -157,7 +142,7 @@ async def index(request: Request, code: str, s: AsyncSession = Depends(get_sessi
157142
if not info:
158143
return {'code': 404, 'msg': f'取件码错误,错误{settings.ERROR_COUNT - ip_error(ip)}次将被禁止10分钟'}
159144
if info.exp_time < datetime.datetime.now() or info.count == 0:
160-
threading.Thread(target=delete_file, args=([{'type': info.type, 'text': info.text}],)).start()
145+
await storage.delete_file({'type': info.type, 'text': info.text})
161146
await s.delete(info)
162147
await s.commit()
163148
return {'code': 404, 'msg': '取件码已过期,请联系寄件人'}
@@ -193,11 +178,11 @@ async def share(text: str = Form(default=None), style: str = Form(default='2'),
193178
exp_count = -1
194179
key = uuid.uuid4().hex
195180
if file:
196-
file_bytes = file.file.read()
181+
file_bytes = await file.read()
197182
size = len(file_bytes)
198183
if size > settings.FILE_SIZE_LIMIT:
199184
return {'code': 404, 'msg': '文件过大'}
200-
_text, _type, name = get_file_name(key, file.filename.split('.')[-1], file, file_bytes)
185+
_text, _type, name = await storage.save_file(file, file_bytes, key), file.content_type, file.filename
201186
else:
202187
size, _text, _type, name = len(text), text, 'text', '文本分享'
203188
info = Codes(

settings.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,3 +25,5 @@
2525
DESCRIPTION = config('DESCRIPTION', cast=str, default="FileCodeBox,文件快递柜,口令传送箱,匿名口令分享文本,文件等文件")
2626

2727
KEYWORDS = config('KEYWORDS', cast=str, default="FileCodeBox,文件快递柜,口令传送箱,匿名口令分享文本,文件等文件")
28+
29+
STORAGE_ENGINE = config('STORAGE_ENGINE', cast=str, default="filesystem")

storage.py

Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
import os
2+
import asyncio
3+
import datetime
4+
from pathlib import Path
5+
6+
import settings
7+
8+
9+
class FileSystemStorage:
10+
DATA_ROOT = Path(settings.DATA_ROOT)
11+
STATIC_URL = settings.STATIC_URL
12+
NAME = "filesystem"
13+
14+
async def get_filepath(self, path):
15+
return self.DATA_ROOT / path.lstrip(self.STATIC_URL + '/')
16+
17+
def _save(self, filepath, file_bytes):
18+
with open(filepath, 'wb') as f:
19+
f.write(file_bytes)
20+
21+
async def save_file(self, file, file_bytes, key):
22+
now = datetime.datetime.now()
23+
path = self.DATA_ROOT / f"upload/{now.year}/{now.month}/{now.day}/"
24+
ext = file.filename.split('.')[-1]
25+
name = f'{key}.{ext}'
26+
if not path.exists():
27+
path.mkdir(parents=True)
28+
filepath = path / name
29+
await asyncio.to_thread(self._save, filepath, file_bytes)
30+
text = f"{self.STATIC_URL}/{filepath.relative_to(self.DATA_ROOT)}"
31+
return text
32+
33+
async def delete_file(self, file):
34+
filepath = self.DATA_ROOT / file['text'].lstrip(self.STATIC_URL + '/')
35+
await asyncio.to_thread(os.remove, filepath)
36+
37+
async def delete_files(self, files):
38+
for file in files:
39+
if file['type'] != 'text':
40+
await self.delete_file(file)
41+
42+
43+
STORAGE_ENGINE = {
44+
"filesystem": FileSystemStorage
45+
}

0 commit comments

Comments
 (0)