22import logging
33import os
44import re
5+ import zipfile
56from typing import Any , BinaryIO , Dict , List , Optional , Union , cast
67from warnings import warn
78
@@ -257,7 +258,7 @@ def get_attachment(self, attachment_id: T_id) -> T_resp_json:
257258
258259 def download_issue_attachments (
259260 self ,
260- issue : str ,
261+ issue : T_id ,
261262 path : Optional [str ] = None ,
262263 overwrite : bool = False ,
263264 stream : bool = False ,
@@ -266,6 +267,8 @@ def download_issue_attachments(
266267 ) -> Optional [str ]:
267268 """
268269 Downloads all attachments from a Jira issue.
270+ This method downloads zip file compressed from Jira server side, and may fail if total attachment size is too large.
271+ Use `get_all_attachment_contents()` to download individual attachments and zip from client side.
269272 :param issue: The issue-key of the Jira issue
270273 :param path: Path to directory where attachments will be saved. If None, current working directory will be used.
271274 :param overwrite: If True, always download and create new zip file.
@@ -378,6 +381,53 @@ def get_attachment_content(self, attachment_id: T_id) -> bytes:
378381 headers = {"Accept" : "*/*" },
379382 )
380383
384+ def get_all_attachment_contents (
385+ self ,
386+ issue : T_id ,
387+ path : Optional [str ] = None ,
388+ overwrite : bool = False ,
389+ compression : int = zipfile .ZIP_STORED ,
390+ ) -> Optional [str ]:
391+ """
392+ Downloads all attachments from a Jira issue by downloading individual files and creating zip file.
393+ This method is useful when total attachment size is too large for Jira server to compress as single file.
394+ If total attachment size is small enough, using `download_issue_attachments()` may be more efficient.
395+ :param issue: The issue-key of the Jira issue
396+ :param path: Path to directory where attachments will be saved. If None, current working directory will be used.
397+ :param overwrite: If True, always download and create new zip file.
398+ If False (default), download will be skipped when zip file already exists in path.
399+ :param compression: Compression method for zipfile. Should be one of the constants listed in documentation page.
400+ https://docs.python.org/3/library/zipfile.html#zipfile.ZipFile
401+ :return: File path of the zip file if file is existing or download is successful. None if attachment does not exist.
402+ """
403+ try :
404+ if path is None :
405+ path = os .getcwd ()
406+ issue_id = self .issue (issue , fields = "id" )["id" ]
407+ attachment_name = f"{ issue_id } _attachments.zip"
408+ file_path = os .path .join (path , attachment_name )
409+ if not overwrite and os .path .isfile (file_path ):
410+ return file_path
411+
412+ attachments_metadata = self .get_attachments_ids_from_issue (issue )
413+ if not attachments_metadata :
414+ return None
415+
416+ with zipfile .ZipFile (file_path , "w" , compression = compression ) as file :
417+ for meta in attachments_metadata :
418+ file .writestr (meta ["filename" ], self .get_attachment_content (meta ["attachment_id" ]))
419+
420+ return file_path
421+
422+ except FileNotFoundError :
423+ raise FileNotFoundError ("Verify if directory path is correct and/or if directory exists" )
424+ except PermissionError :
425+ raise PermissionError (
426+ "Directory found, but there is a problem with saving file to this directory. Check directory permissions"
427+ )
428+ except Exception as e :
429+ raise e
430+
381431 def remove_attachment (self , attachment_id : T_id ) -> T_resp_json :
382432 """
383433 Remove an attachment from an issue
0 commit comments