3232from typing import (
3333 TYPE_CHECKING ,
3434 Any ,
35+ AsyncGenerator ,
3536 Callable ,
3637 ClassVar ,
3738 Sequence ,
@@ -290,6 +291,7 @@ async def save(
290291 * ,
291292 seek_begin : bool = True ,
292293 use_cached : bool = False ,
294+ chunksize : int | None = None ,
293295 ) -> int :
294296 """|coro|
295297
@@ -311,6 +313,8 @@ async def save(
311313 after the message is deleted. Note that this can still fail to download
312314 deleted attachments if too much time has passed, and it does not work
313315 on some types of attachments.
316+ chunksize: Optional[:class:`int`]
317+ The maximum size of each chunk to process.
314318
315319 Returns
316320 -------
@@ -323,16 +327,33 @@ async def save(
323327 Saving the attachment failed.
324328 NotFound
325329 The attachment was deleted.
330+ InvalidArgument
331+ Argument `chunksize` is less than 1.
326332 """
327- data = await self .read (use_cached = use_cached )
333+ if chunksize is not None :
334+ data = self .read_chunked (use_cached = use_cached , chunksize = chunksize )
335+ else :
336+ data = await self .read (use_cached = use_cached )
337+
328338 if isinstance (fp , io .BufferedIOBase ):
329- written = fp .write (data )
339+ if chunksize :
340+ written = 0
341+ async for chunk in data :
342+ written += fp .write (chunk )
343+ else :
344+ written = fp .write (data )
330345 if seek_begin :
331346 fp .seek (0 )
332347 return written
333348 else :
334349 with open (fp , "wb" ) as f :
335- return f .write (data )
350+ if chunksize :
351+ written = 0
352+ async for chunk in data :
353+ written += f .write (chunk )
354+ return written
355+ else :
356+ return f .write (data )
336357
337358 async def read (self , * , use_cached : bool = False ) -> bytes :
338359 """|coro|
@@ -369,6 +390,45 @@ async def read(self, *, use_cached: bool = False) -> bytes:
369390 data = await self ._http .get_from_cdn (url )
370391 return data
371392
393+ async def read_chunked (
394+ self , chunksize : int , * , use_cached : bool = False
395+ ) -> AsyncGenerator [bytes ]:
396+ """|coro|
397+
398+ Retrieves the content of this attachment in chunks as a :class:`AsyncGenerator` object of bytes.
399+
400+ Parameters
401+ ----------
402+ chunksize: :class:`int`
403+ The maximum size of each chunk to process.
404+ use_cached: :class:`bool`
405+ Whether to use :attr:`proxy_url` rather than :attr:`url` when downloading
406+ the attachment. This will allow attachments to be saved after deletion
407+ more often, compared to the regular URL which is generally deleted right
408+ after the message is deleted. Note that this can still fail to download
409+ deleted attachments if too much time has passed, and it does not work
410+ on some types of attachments.
411+
412+ Yields
413+ ------
414+ :class:`bytes`
415+ A chunk of the file.
416+
417+ Raises
418+ ------
419+ HTTPException
420+ Downloading the attachment failed.
421+ Forbidden
422+ You do not have permissions to access this attachment
423+ NotFound
424+ The attachment was deleted.
425+ InvalidArgument
426+ Argument `chunksize` is less than 1.
427+ """
428+ url = self .proxy_url if use_cached else self .url
429+ async for chunk in self ._http .stream_from_cdn (url , chunksize ):
430+ yield chunk
431+
372432 async def to_file (self , * , use_cached : bool = False , spoiler : bool = False ) -> File :
373433 """|coro|
374434
0 commit comments