#python #django-rest-framework #websocket #django-channels #channels
Вопрос:
Я пытаюсь добавить канал в свой проект, чтобы отправить процент загрузки. У меня есть метод download_file()
, который загружает файл на сервер с нескольких конечных точек.
Мне нужно добавить каналы в проект, но это API без интерфейса.
asgi.py
import os import django from django.core.asgi import get_asgi_application from src.apps import api from channels.routing import ProtocolTypeRouter, URLRouter os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'src.settings') django.setup() application = ProtocolTypeRouter({ 'http': get_asgi_application(), 'websocket': URLRouter(api.routing.websocket_urlpatterns), })
settings.py
THIRD_PARTY_APPS = ( '...' , 'channels', ) # ... # channels ASGI_APPLICATION = 'src.asgi.application' CHANNEL_LAYERS = { "default": { "BACKEND": "channels_rabbitmq.core.RabbitmqChannelLayer", }, }
routing.py
from django.urls import path from src.apps.api.consumers import DownloadStateConsumer websocket_urlpatterns = [ path('download_state/', DownloadStateConsumer.as_asgi()), ]
consumers.py
from channels.generic.websocket import AsyncWebsocketConsumer class DownloadStateConsumer(AsyncJsonWebsocketConsumer): async def connect(self): await self.accept() async def receive(self, text_data=None, bytes_data=None, **kwargs): await self.send_json({'message': text_data})
file_download.py
def download_file(): # ... response = requests.get(link.link, stream=True) total_length = response.headers.get('content-length') size = 0 with response as r: r.raise_for_status() with open(filename, 'wb') as f: state = -1 for chunk in r.iter_content(chunk_size=8192): size = len(chunk) f.write(chunk) done = int(100 * size / int(total_length)) sys.stdout.write("r%s%s" % (str(done), "%")) if done != state: async_to_sync( get_channel_layer().group_send( 'download_state', { 'type': 'send.percent', 'message': done, } ) ) state = done sys.stdout.flush() # ...
Я понимаю, что на клиенте сценарий должен быть примерно таким
const webSocket = new WebSocket( 'ws://' window.location.host '/download_state/' ); webSocket.onmessage = function(e) { const data = JSON.parse(e.data); document.querySelector('#download-state').value = data.message; }; webSocket.onclose = function(e) { console.error('Chat socket closed unexpectedly'); };
Вопрос в том, как отправить состояние загрузки клиента из download_file()
метода? Текущий способ не работает — клиент не получает сообщений. Спасибо!
Ответ №1:
В конце концов, я перешел на channels_redis
instead channels_rabbitmq
:
CHANNEL_LAYERS = { 'default': { 'BACKEND': 'channels_redis.core.RedisChannelLayer', 'CONFIG': { "hosts": [('127.0.0.1', 6379)], }, }, }
Я запустил изображение redis в Docker на указанном порту, и теперь оно работает.