Download media after Background tasks ends

Hello

I am importing Excel file and doing some calculation, the file is temporary and is not saved in table. At the end the file is downloaded to the user.
This works perfectly but if the Excel file is big and the process time exceed 30 seconds the server timeout so I tried to use Background Tasks.

I am getting this error:
AttributeError: ‘LiveObjectProxy’ object has no attribute ‘url’

I also searched in forum and found the topic below related months ago, but I wanted to know if there is an update or simple way to download the Media file to client after the task ends

Code Sample:

@anvil.server.background_task
def make_slow_request():
  tmp_name = "/tmp/%s......
.
.
  X_media = anvil.media.from_file(filename)
  return X_media

@anvil.server.callable
def store_data_new(file):
  task = anvil.server.launch_background_task('make_slow_request')
  return task 

def file_loader_1_change(self, file, **event_args):
    excel = anvil.server.call('store_data_new',file)
    download(excel) 

Background tasks do not work that way. What you have returned is an object which contains the information about the task

I don’t think there are any alternative ways to do this

import time
def file_loader_1_change(self, file, **event_args):
    excel = anvil.server.call('store_data_new',file)
    while not excel.is_completed():
         time.sleep(1)
     
    download(excel.get_return_value())

And if you want something more efficient (which does not block your code), consider using a timer component.

Also, please consider checking out the docs Anvil Docs | Background Tasks

I am not an expert in this field, so I will use Uplink instead.

Thank you

I have a background task that creates a PDF file and saves it in a data table and a scheduled task that deleted all the rows older than an hour of that table every night.

The download on the form is automatically triggered by a timer that checks the status every second.

I think I haven’t learned well how background task works, because I removed the part that download the Excel file(that causes the error) to test it and it fails and I don’t get any information in App logs. Maybe I am not passing the file correctly.
Do you create PDF file from uploading an Excel?

If you are on the free plan and wish to workaround the 30 seconds limitation, this may be the beginning of a path to the simplest solution, as @stefano.menci said.

Here is a link to another question about how to store files in a data table, where I made them a clone link to see an example of how it works.

Edit:
Once you have it in a data table and working, you could:

  1. Use uplink as you have suggested. You don’t actually HAVE to run uplink as anvil.server.run_forever() as the docs suggest. You could
  2. Run a while loop with a time.sleep() function that checks your anvil data table for a file to process every 5 seconds or so, then
  3. does that work that takes longer than 30 seconds, and
  4. returns it marking it as done in a bool column of the table. You could then
  5. set a timer component on your anvil app to check to see if the file is done and
  6. create the link to the data table for the user on the anvil form.

Edit 2:
Forgot to add this about background tasks:
Here is a link to another cloneable example app I created that is about a different topic (async components) but shows off working background tasks quite nicely.

Here is an overview of my workflow:

  • The PdfFiles table has 3 columns: id, status and file.
  • The form calls a server callable start_pdf_generation(some_id) and sets the interval of a timer to 1.
  • The server function start_pdf_generation starts the background task pdf_generation.
  • The pdf_generation background task adds a row to the PdfFiles table with the id and the status, then it starts collecting info (it can take a minute ot two). While collecting info it updates the status on that row. When all the info is ready, it uses it to render the PDF, then stores it to the file column and sets the status to 'done'.
  • While that happens, the tick event of the timer keeps calling a server callable get_pdf_generation_status(some_id) and updating a self.status_label.text.
  • When the status is 'done', the get_pdf_generation_status returns the PDF file for download and the tick event sets the interval back to 0 to stop the polling.

I use a column for the status instead of the task status, because I was having some problems with checking the task status. I don’t remember the details, perhaps it was too slow? The disadvantage is that, if the status on the table never changes, you don’t know if it’s because the background task crashed or because it is genuinely taking a long time.

These are little implementation details, and you can play around with it. I hope you get the idea. On one end it’s more difficult than it should be. On the other hand you have all the freedom and control over what you can do.

1 Like

Very impressed, thank you