forked from devmanorg/async-download-service
-
Notifications
You must be signed in to change notification settings - Fork 0
/
server.py
114 lines (95 loc) · 3.35 KB
/
server.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
import aiofiles
import argparse
import asyncio
import os
import logging
from aiohttp import web
from functools import partial
from environs import Env
INTERVAL_SECS = 1
ARCHIVE_BATCH_SIZE = 100
logger = logging.getLogger(__name__)
async def handle_index_page(request):
async with aiofiles.open('index.html', mode='r') as index_file:
index_contents = await index_file.read()
return web.Response(text=index_contents, content_type='text/html')
async def initialize_archiving(archive_hash, photos_dir_path):
archive_path = os.path.join(photos_dir_path, archive_hash)
if not os.path.exists(archive_path):
raise web.HTTPNotFound(text='Архив не существует или был удален')
process = await asyncio.create_subprocess_exec(
'zip', '-', '-r', '.',
stdout=asyncio.subprocess.PIPE,
stderr=asyncio.subprocess.PIPE,
cwd=archive_path
)
return process
async def recieve_arhcive(
request,
response_delay,
photos_dir_path,
outgoing_archive_name):
archive_hash = request.match_info['archive_hash']
process = await initialize_archiving(archive_hash, photos_dir_path)
response = web.StreamResponse()
response.headers['Content-Type'] = 'text/html'
response.headers['Content-Disposition'] = (
'attachment;'
f'filename="{outgoing_archive_name}"'
)
await response.prepare(request)
try:
while not process.stdout.at_eof():
batch = await process.stdout.read(ARCHIVE_BATCH_SIZE*1024)
logger.info('Sending archive chunk ...')
if response_delay:
await asyncio.sleep(response_delay)
await response.write(batch)
except asyncio.CancelledError:
logger.warning('Download was interrupted')
raise
finally:
if process.returncode:
process.kill()
await process.communicate()
return response
if __name__ == '__main__':
env = Env()
env.read_env()
photos_dir_path = env.str('PHOTOS_DIR_PATH', 'test_photos')
outgoing_archive_name = env.str('OUTGOING_ARCHIVE_NAME', 'test_photos.zip')
parser = argparse.ArgumentParser()
parser.add_argument(
'--response_delay',
help='Задержка перед отправкой порции данных',
type=int,
default=0,
)
parser.add_argument(
'--skip_logging',
help='Подробное логгирование, по умолчанию True.'
'Если указать значение False, будут выводиться'
'сообщения уровня WARNING и выше',
action='store_true',
default=False
)
args = parser.parse_args()
logging_level = logging.DEBUG if not args.skip_logging else logging.WARNING
logging.basicConfig(
format='%(asctime)s - %(levelname)s - %(message)s',
level=logging_level
)
app = web.Application()
app.add_routes([
web.get('/', handle_index_page),
web.get(
'/archive/{archive_hash}/',
partial(
recieve_arhcive,
response_delay=args.response_delay,
photos_dir_path=photos_dir_path,
outgoing_archive_name=outgoing_archive_name
)
),
])
web.run_app(app)