Files
amq-adapter-python/amqp/adapter/file_uploader.py
T

75 lines
2.9 KiB
Python

import logging
from asyncio import AbstractEventLoop
import aio_pika
import asyncio
import os
from pathlib import Path
from aio_pika.abc import AbstractRobustChannel, AbstractRobustExchange
from aio_pika import Message, DeliveryMode
from amqp.adapter.file_downloader import IN_PROGRESS, END_OF_FILE
from amqp.router.router_base import AMQ_REPLY_TO_EXCHANGE
async def publish_file_to_rabbitmq(
exchange: AbstractRobustExchange,
file_path: Path,
routing_key: str,
loop: AbstractEventLoop,
max_chunk_size: int = 2**20, # 1MB default chunk size
content_type: str = 'application/octet-stream', # Default content type
delivery_mode: DeliveryMode = DeliveryMode.PERSISTENT,
) -> tuple[str, int]:
"""
Reads a file from the file system and publishes its content to a RabbitMQ exchange
in chunks. It declares a queue with a random name, binds it to the specified
exchange, and uses the queue name as the routing key.
Args:
exchange: The aio_pika RobustChannel to use for communication with RabbitMQ.
file_path: The path to the file to publish.
max_chunk_size: The maximum size of each chunk (in bytes).
routing_key: The name of the RabbitMQ exchange routing key to publish to correct queue.
loop: correct event loop
max_chunk_size: chunk size to send
content_type: The content type of the file data.
delivery_mode: The delivery mode (e.g., PERSISTENT or TRANSIENT).
Returns:
The name of the randomly generated queue where the file content was published.
"""
if not file_path.is_file():
raise FileNotFoundError(f"File not found: {file_path}")
_file_size = os.path.getsize(file_path)
with open(file_path, 'rb') as f:
_offset = 0
while True:
_chunk = f.read(max_chunk_size)
if not _chunk:
break # End of file
message = Message(
body=_chunk,
content_type=content_type,
delivery_mode=delivery_mode,
headers={
'file_name': file_path.name,
'offset': _offset,
'total_size': _file_size,
'chunk_size': len(_chunk),
'eof': END_OF_FILE if max_chunk_size + _offset >= _file_size else IN_PROGRESS
}
)
_future = asyncio.run_coroutine_threadsafe(exchange.publish(message, routing_key=routing_key), loop)
while not _future.done():
# sleep allows coroutine to execute. must wait for completion to ensure all chunks written in right order
await asyncio.sleep(0.02)
_offset += len(_chunk)
logging.debug(f"Published chunk {_offset}/{_file_size} to {exchange.name}:{routing_key}")
print(f"File {file_path} published to RabbitMQ exchange {exchange.name}, queue {routing_key}")
return routing_key, _file_size