2323 TypeVar ,
2424 Union ,
2525)
26+ from io import BytesIO
2627
2728import aiohttp
2829from aleph_message .models import (
3132 AlephMessage ,
3233 ForgetContent ,
3334 ForgetMessage ,
35+ ItemHash ,
3436 ItemType ,
3537 MessageType ,
3638 PostContent ,
4345)
4446from aleph_message .models .execution .base import Encoding
4547from aleph_message .status import MessageStatus
46- from pydantic import ValidationError
48+ from pydantic import ValidationError , BaseModel
4749
4850from aleph .sdk .types import Account , GenericMessage , StorageEnum
49-
51+ from aleph . sdk . utils import copy_async_readable_to_buffer , Writable , AsyncReadable
5052from .conf import settings
5153from .exceptions import (
5254 BroadcastError ,
5355 InvalidMessageError ,
5456 MessageNotFoundError ,
5557 MultipleMessagesError ,
58+ FileTooLarge ,
5659)
5760from .models import MessagesResponse
5861from .utils import check_unix_socket_valid , get_message_type_value
@@ -229,6 +232,12 @@ def get_posts(
229232 def download_file (self , file_hash : str ) -> bytes :
230233 return self ._wrap (self .async_session .download_file , file_hash = file_hash )
231234
235+ def download_file_ipfs (self , file_hash : str ) -> bytes :
236+ return self ._wrap (
237+ self .async_session .download_file_ipfs ,
238+ file_hash = file_hash ,
239+ )
240+
232241 def watch_messages (
233242 self ,
234243 message_type : Optional [MessageType ] = None ,
@@ -609,6 +618,55 @@ async def get_posts(
609618 resp .raise_for_status ()
610619 return await resp .json ()
611620
621+ async def download_file_to_buffer (
622+ self ,
623+ file_hash : str ,
624+ output_buffer : Writable [bytes ],
625+ ) -> None :
626+ """
627+ Download a file from the storage engine and write it to the specified output buffer.
628+ :param file_hash: The hash of the file to retrieve.
629+ :param output_buffer: Writable binary buffer. The file will be written to this buffer.
630+ """
631+
632+ async with self .http_session .get (
633+ f"/api/v0/storage/raw/{ file_hash } "
634+ ) as response :
635+ if response .status == 200 :
636+ await copy_async_readable_to_buffer (
637+ response .content , output_buffer , chunk_size = 16 * 1024
638+ )
639+ if response .status == 413 :
640+ ipfs_hash = ItemHash (file_hash )
641+ if ipfs_hash .item_type == ItemType .ipfs :
642+ return await self .download_file_ipfs_to_buffer (
643+ file_hash , output_buffer
644+ )
645+ else :
646+ raise FileTooLarge (f"The file from { file_hash } is too large" )
647+
648+ async def download_file_ipfs_to_buffer (
649+ self ,
650+ file_hash : str ,
651+ output_buffer : Writable [bytes ],
652+ ) -> None :
653+ """
654+ Download a file from the storage engine and write it to the specified output buffer.
655+
656+ :param file_hash: The hash of the file to retrieve.
657+ :param output_buffer: The binary output buffer to write the file data to.
658+ """
659+ async with aiohttp .ClientSession () as session :
660+ async with session .get (
661+ f"https://ipfs.aleph.im/ipfs/{ file_hash } "
662+ ) as response :
663+ if response .status == 200 :
664+ await copy_async_readable_to_buffer (
665+ response .content , output_buffer , chunk_size = 16 * 1024
666+ )
667+ else :
668+ response .raise_for_status ()
669+
612670 async def download_file (
613671 self ,
614672 file_hash : str ,
@@ -620,11 +678,24 @@ async def download_file(
620678
621679 :param file_hash: The hash of the file to retrieve.
622680 """
623- async with self .http_session .get (
624- f"/api/v0/storage/raw/{ file_hash } "
625- ) as response :
626- response .raise_for_status ()
627- return await response .read ()
681+ buffer = BytesIO ()
682+ await self .download_file_to_buffer (file_hash , output_buffer = buffer )
683+ return buffer .getvalue ()
684+
685+ async def download_file_ipfs (
686+ self ,
687+ file_hash : str ,
688+ ) -> bytes :
689+ """
690+ Get a file from the ipfs storage engine as raw bytes.
691+
692+ Warning: Downloading large files can be slow.
693+
694+ :param file_hash: The hash of the file to retrieve.
695+ """
696+ buffer = BytesIO ()
697+ await self .download_file_ipfs_to_buffer (file_hash , output_buffer = buffer )
698+ return buffer .getvalue ()
628699
629700 async def get_messages (
630701 self ,
0 commit comments