Downloading binary file from API [BUG?]

Hi, new to Anvil and would appreciate advice.

I’m trying to read a file directly from an API (can be CSV, XLSX or ZIP) and save it to the user’s local machine. So far I’ve tried

@anvil.server.callable
def DownloadFile():
  filename = 'test.csv'
  url = base_url + '/file/' + filename
  headers={'Content-Type': 'application/octet-stream'}
  response = anvil.http.request(url, method="GET", headers=headers, username=username, password=password)   
  return response

on the server.

Then in a form’s event handler

def button_1_click(self, **event_args):
      r = anvil.server.call('DownloadFile')      
      r1 = BlobMedia(r.content_type, r.get_bytes(), name='test.csv')
      anvil.media.download(r1)

This works for CSV files, but not for XLSX or ZIP files, which appear to have a lot of extra characters inserted. Is there a way to keep files unchanged here?

In addition, is there a way to invoke the ‘Save As’ dialog for the file so the user can choose where to put it etc?

Any tips much appreciated.

Andrew Colin.

What happens if you just:

def button_1_click(self, **event_args):
      r = anvil.server.call('DownloadFile')      
      anvil.media.download(r)

The result of an anvil.http.request() should already be a media object.

If that does not work, try putting r in a link and see if that makes a difference. (It shouldn’t)

1 Like

Calling anvil.http.request appears to corrupt an existing media object.

For instance, I ran

@anvil.server.callable
def DownloadFile():
  filename = 'FTREER.jpg'
  url = base_url + '/file/' + filename  
  response = anvil.http.request(url, method="GET", json=False, username=username, password=password)
  print (response.length)
  return response

The size of the jpg file is 99 kb on my server, but is 182 kb after running the above code. The issue also applies to XLSX and ZIP files. I suspect the issue is something to do with UTF-8 encoding as the downloaded file has lots of ‘\x’ characters.

I’ve tried setting

headers={'content-type': 'application/octet-stream'}

and

headers={'content-type': 'Content-type: image/jpeg'}

but no luck.

I can download the file from stand-alone Python code using the requests library, so I know the file mechanics are working OK:

def DownloadFile (filename, username):
    url = base_url + '/file/' + filename          
    response = requests.get(url, stream=True, auth=HTTPBasicAuth(username, password))         
    with open(filename, 'wb') as f:
        for chunk in response.iter_content(chunk_size = 1024):
            f.write(chunk)
    f.close()
    print(response.status_code, 'Downloaded', filename)
        
    return response.status_code 

Can you suggest a fix or a work-around?

Thanks

Andrew.

Can you please flag this as a bug (‘Calling anvil.http.request appears to corrupt an existing media object’). I’ve been through the forums several times but have been unable to find a fix.

Thanks!

Andrew.

For anyone else who has this issue, here is a workaround I found, using the requests library and the tmp directory, rather than anvil.http.request.

On the server:

@anvil.server.callable
def DownloadFile():
  filename = 'test.zip'
  url = base_url + '/file/' + filename        
  response = requests.get(url, stream=True, auth=HTTPBasicAuth(username, password))         
  with open('/tmp/' + filename, 'wb') as f:
    f.write (response.content)
  f.close()
  X_media = anvil.media.from_file('/tmp/' + filename, 'application/octet-stream', filename)
  return X_media

On the client

def button_1_click(self, **event_args):
      x_media =anvil.server.call('DownloadFile')
      anvil.media.download(x_media)

I’d be keen to know whether there is a simpler way to do this.

A BlobMedia can be constructed from a byte string. If you can get a byte string from the requests object, then you should be able to bypass the temporary file.