Starchik1 commited on
Commit
341d0b4
·
verified ·
1 Parent(s): 448021a

Update main.py

Browse files
Files changed (1) hide show
  1. main.py +177 -32
main.py CHANGED
@@ -3,63 +3,208 @@ import random
3
  import string
4
  import threading
5
  import time
 
6
  from datetime import datetime, timedelta
 
7
 
8
  app = Flask(__name__)
9
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
10
  transfers = {}
11
  transfer_data = {}
12
  transfer_lock = threading.Lock()
13
 
14
- # Настройки
15
- KEEP_ALIVE_INTERVAL = 10 # Отправлять keep-alive каждые 25 секунд
16
- TRANSFER_TIMEOUT = 3600 # 1 час таймаута передачи
 
 
 
 
 
 
 
 
 
 
 
 
17
 
18
- def generate_short_id(length=4):
 
 
 
 
19
  while True:
20
  token = ''.join(random.choices(string.ascii_letters + string.digits, k=length))
21
  if token not in transfers:
22
  return token
23
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
24
  @app.route('/download/<transfer_id>', methods=['GET'])
25
  def download_file(transfer_id):
 
26
  def generate():
27
- last_activity = time.time()
28
- index = 0
29
-
30
- while True:
31
- with transfer_lock:
32
- # Проверка таймаута
33
- if time.time() - last_activity > TRANSFER_TIMEOUT:
34
- break
35
-
36
- transfer = transfers.get(transfer_id)
37
- if not transfer:
38
- break
39
-
40
- chunks = transfer_data.get(transfer_id, [])
 
 
 
 
 
 
 
 
 
 
 
 
 
41
 
42
- # Отправка доступных чанков
43
- while index < len(chunks):
44
- yield chunks[index]
45
  last_activity = time.time()
46
- index += 1
47
 
48
- if transfer['completed']:
49
- break
50
 
51
- # Отправка keep-alive
52
- if time.time() - last_activity > KEEP_ALIVE_INTERVAL:
53
- yield b'\0' # Пустой чанк для поддержания соединения
54
- last_activity = time.time()
55
-
56
- time.sleep(0.1)
57
 
 
58
  return Response(
59
  generate(),
60
  mimetype='application/octet-stream',
61
  headers={
62
- 'Content-Disposition': f'attachment; filename="{transfers[transfer_id]["filename"]}"',
63
- 'Cache-Control': 'no-store'
 
64
  }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
65
  )
 
3
  import string
4
  import threading
5
  import time
6
+ from collections import deque
7
  from datetime import datetime, timedelta
8
+ import logging
9
 
10
  app = Flask(__name__)
11
 
12
+ # Конфигурация
13
+ TRANSFER_LIFETIME = timedelta(hours=1) # Время хранения завершенных передач
14
+ CLEANUP_INTERVAL = 3600 # Интервал очистки (5 минут)
15
+ MAX_CHUNK_SIZE = 100 * 1024 * 1024 # 100MB
16
+ KEEP_ALIVE_INTERVAL = 25 # Интервал keep-alive (секунды)
17
+ TRANSFER_TIMEOUT = 3600 # Таймаут передачи (1 час)
18
+
19
+ # Логирование
20
+ logging.basicConfig(
21
+ level=logging.INFO,
22
+ format='%(asctime)s - %(levelname)s - %(message)s'
23
+ )
24
+ logger = logging.getLogger(__name__)
25
+
26
+ # Хранилище данных
27
  transfers = {}
28
  transfer_data = {}
29
  transfer_lock = threading.Lock()
30
 
31
+ def cleanup_task():
32
+ """Фоновая задача для очистки старых передач"""
33
+ while True:
34
+ time.sleep(CLEANUP_INTERVAL)
35
+ with transfer_lock:
36
+ now = datetime.now()
37
+ to_delete = []
38
+ for transfer_id, info in transfers.items():
39
+ if info['completed'] and (now - info['completed_time']) > TRANSFER_LIFETIME:
40
+ to_delete.append(transfer_id)
41
+ logger.info(f"Cleaning up transfer: {transfer_id}")
42
+
43
+ for transfer_id in to_delete:
44
+ del transfers[transfer_id]
45
+ del transfer_data[transfer_id]
46
 
47
+ # Запуск фоновой задачи
48
+ threading.Thread(target=cleanup_task, daemon=True).start()
49
+
50
+ def generate_short_id(length=8):
51
+ """Генерация уникального ID для передачи"""
52
  while True:
53
  token = ''.join(random.choices(string.ascii_letters + string.digits, k=length))
54
  if token not in transfers:
55
  return token
56
 
57
+ @app.route('/create_transfer', methods=['POST'])
58
+ def create_transfer():
59
+ """Создание новой передачи"""
60
+ try:
61
+ data = request.json
62
+ if not data or 'filename' not in data:
63
+ return jsonify({'error': 'Missing required fields'}), 400
64
+
65
+ transfer_id = generate_short_id()
66
+
67
+ with transfer_lock:
68
+ transfers[transfer_id] = {
69
+ 'filename': data['filename'],
70
+ 'filesize': data.get('filesize', 0),
71
+ 'completed': False,
72
+ 'created_time': datetime.now(),
73
+ 'completed_time': None,
74
+ 'last_activity': datetime.now()
75
+ }
76
+ transfer_data[transfer_id] = deque(maxlen=1000)
77
+
78
+ logger.info(f"Created new transfer: {transfer_id}")
79
+ return jsonify({'transfer_id': transfer_id})
80
+
81
+ except Exception as e:
82
+ logger.error(f"Error creating transfer: {str(e)}")
83
+ return jsonify({'error': str(e)}), 500
84
+
85
+ @app.route('/upload/<transfer_id>', methods=['POST'])
86
+ def upload_chunk(transfer_id):
87
+ """Загрузка чанка данных"""
88
+ try:
89
+ if transfer_id not in transfers:
90
+ return jsonify({'error': 'Invalid transfer ID'}), 404
91
+
92
+ chunk = request.data
93
+ if len(chunk) > MAX_CHUNK_SIZE:
94
+ return jsonify({'error': 'Chunk size too large'}), 413
95
+
96
+ with transfer_lock:
97
+ if transfers[transfer_id]['completed']:
98
+ return jsonify({'error': 'Transfer already completed'}), 400
99
+
100
+ transfer_data[transfer_id].append(chunk)
101
+ transfers[transfer_id]['last_activity'] = datetime.now()
102
+
103
+ if request.headers.get('X-Transfer-Complete') == 'true':
104
+ transfers[transfer_id]['completed'] = True
105
+ transfers[transfer_id]['completed_time'] = datetime.now()
106
+ logger.info(f"Transfer completed: {transfer_id}")
107
+
108
+ return jsonify({'status': 'chunk accepted'})
109
+
110
+ except Exception as e:
111
+ logger.error(f"Error uploading chunk: {str(e)}")
112
+ return jsonify({'error': str(e)}), 500
113
+
114
  @app.route('/download/<transfer_id>', methods=['GET'])
115
  def download_file(transfer_id):
116
+ """Скачивание файла"""
117
  def generate():
118
+ try:
119
+ index = 0
120
+ last_activity = time.time()
121
+
122
+ while True:
123
+ with transfer_lock:
124
+ # Проверка таймаута
125
+ if time.time() - last_activity > TRANSFER_TIMEOUT:
126
+ logger.warning(f"Transfer timeout: {transfer_id}")
127
+ break
128
+
129
+ transfer = transfers.get(transfer_id)
130
+ if not transfer:
131
+ logger.warning(f"Transfer not found: {transfer_id}")
132
+ break
133
+
134
+ chunks = transfer_data.get(transfer_id, [])
135
+
136
+ # Отправка доступных чанков
137
+ while index < len(chunks):
138
+ yield chunks[index]
139
+ last_activity = time.time()
140
+ index += 1
141
+
142
+ if transfer['completed']:
143
+ logger.info(f"Transfer finished: {transfer_id}")
144
+ break
145
 
146
+ # Отправка keep-alive
147
+ if time.time() - last_activity > KEEP_ALIVE_INTERVAL:
148
+ yield b'\0' # Пустой чанк для поддержания соединения
149
  last_activity = time.time()
 
150
 
151
+ time.sleep(0.5)
 
152
 
153
+ except Exception as e:
154
+ logger.error(f"Download error: {str(e)}")
155
+
156
+ if transfer_id not in transfers:
157
+ return jsonify({'error': 'Transfer not found'}), 404
 
158
 
159
+ transfer = transfers[transfer_id]
160
  return Response(
161
  generate(),
162
  mimetype='application/octet-stream',
163
  headers={
164
+ 'Content-Disposition': f'attachment; filename="{transfer["filename"]}"',
165
+ 'Cache-Control': 'no-store',
166
+ 'Transfer-Encoding': 'chunked'
167
  }
168
+ )
169
+
170
+ @app.route('/status/<transfer_id>', methods=['GET'])
171
+ def transfer_status(transfer_id):
172
+ """Получение статуса передачи"""
173
+ with transfer_lock:
174
+ if transfer_id not in transfers:
175
+ return jsonify({'error': 'Transfer not found'}), 404
176
+
177
+ transfer = transfers[transfer_id]
178
+ return jsonify({
179
+ 'filename': transfer['filename'],
180
+ 'filesize': transfer['filesize'],
181
+ 'completed': transfer['completed'],
182
+ 'created_time': transfer['created_time'].isoformat(),
183
+ 'last_activity': transfer['last_activity'].isoformat(),
184
+ 'chunks_count': len(transfer_data.get(transfer_id, []))
185
+ })
186
+
187
+ @app.route('/list', methods=['GET'])
188
+ def list_transfers():
189
+ """Список активных передач"""
190
+ with transfer_lock:
191
+ return jsonify({
192
+ 'transfers': [
193
+ {
194
+ 'id': k,
195
+ 'filename': v['filename'],
196
+ 'completed': v['completed'],
197
+ 'created_time': v['created_time'].isoformat()
198
+ }
199
+ for k, v in transfers.items()
200
+ ]
201
+ })
202
+
203
+ if __name__ == '__main__':
204
+ # Конфигурация сервера
205
+ app.run(
206
+ host='0.0.0.0',
207
+ port=5000,
208
+ threaded=True,
209
+ debug=False
210
  )