Don’t hold you’re breath as they’re surprisingly unable to add basic features for years.
Adding one more voice to the peanut gallery. I am reviving this topic to continue a request made over and over. Please add this basic feature to the platform. And if there’s a reason why it’s not being implemented, kindly let us know.
Every request like this one is being reconsidered often, Asana will never say “we won’t do this ever” because that’s never the case.
1 Like
This new application may assist with some of the issues mentioned here if you have any questions let me konw
1 Like
Hello.
What is the progress on this issue?
I think everyone is looking forward to the bulk download feature.
As a workaround, I wrote a Python code to download all files from a task.
Requirements:
- add personal access token to path (environment variables)
- install asana Python client
How to use:
- Run the code from terminal
cd <your location>
python3 download_all_files_from_asana_task.py
- Paste the task ID or URL and press Enter
Code:
import asana
from asana.rest import ApiException
import os
import re
import requests
import time
configuration = asana.Configuration()
configuration.access_token = os.environ['ASANA_ACCESS_TOKEN']
api_client = asana.ApiClient(configuration)
list_regex_task_id_and_url = [
r'^(\d+)$',
r'/task/(\d+)',
r'inbox/\d+/item/(\d+)',
r'&child=(\d+)',
r'app.asana.com/0/\d+/(\d+)',
]
def find_task_gid_from_input(text):
for regex in list_regex_task_id_and_url:
match = re.search(regex, text)
if match:
return match.group(1)
print('\nNot a valid task ID or URL. Please try again.')
return
def get_available_filename(directory, base_filename):
name, ext = os.path.splitext(base_filename)
filename = base_filename
counter = 0
while os.path.exists(os.path.join(directory, filename)):
counter += 1
filename = f"{name} ({counter}){ext}"
return os.path.join(directory, filename)
print('You can download attachments from a task. Press Ctrl+C to exit the program.\n')
while True:
task_input = input('Enter task ID or URL:\n')
task_gid = find_task_gid_from_input(task_input)
if not task_gid:
continue
attachments_api_instance = asana.AttachmentsApi(api_client)
opts = {
'opt_fields': 'download_url,name'
}
print('\nDownloading files...')
try:
api_response = attachments_api_instance.get_attachments_for_object(task_gid, opts)
downloads_dir = os.path.join(os.path.expanduser("~"), "downloads")
for data in api_response:
r = requests.get(data['download_url'])
filepath = get_available_filename(downloads_dir, data['name'])
with open(filepath, 'wb') as f:
f.write(r.content)
time.sleep(0.4)
except ApiException as e:
print("Exception when calling AttachmentsApi->get_attachments_for_object: %s\n" % e)
Note:
- Sleeping 0.4 second supports the rate limit in Free domain. Paid domains can set it to 0.04. Source: Rate limits.
- EDIT on June 9, 2025: Same file name is downloaded with collision suffix like
(1)
.
4 Likes