Serving images through URLs

Hi There,

Has anyone tried using their Anvil app for hosting images, calling the URL for those images through an API to an external program and then trying to render that URL? My app is still in dev and I am not sure if that is having an effect but if I try and search for a stored image’s URL I get blocked, my app can’t be found or I may be logged out.

Can anyone enlighten me as to what to do here? I want to be able to get to those images through a network connection.

Thanks,

James

Is something like this what you had in mind? I am making use of Anvil’s HTTP endpoint feature.

# In server
@anvil.server.http_endpoint('/get_image')
def get_image():

  # images stored as media in DataTable
  media=app_tables.my_images.get(name='cat1')['image']
  return media

If I visit the URL that Anvil provides in the IDE (specified with my “path”), it returns the image in my browser (e.g., my_app_url/get_image).

I may have to look at doing something like this. It looks like you are returning the actual media file whereas I am trying to pass a data table row with many image URLs as columns, for them to be picked up by my Flutter mobile app which has a package that will render a network image from its URL - I was hoping it would be as simple as sending the URL out to my mobile app for it to render but I am being blocked, does Anvil keep the stored media behind a security layer rather than exposing the files when someone has the right URL?

Edit: To be clear I am already using a http_endpoint, the image url is being transferred over as a string, I just can’t do anything with that string at this point and I am not sure what the issues surrounding it are.

I’m not sure I understand exactly what you mean, but as far as I can gather, if you have the URL, you get the image. In that sense there doesn’t seem to be any additional layer of security (I could be wrong). If you would like, you can authenticate users by adding authenticate_users=True to your endpoint decorator.

Are you able to share a basic clone? Even if you are returning a string, it should show up if the endpoint is set up correctly I would think. I’m just wondering if there is some other issue with the endpoint URL and/or path.

I’ve built a lot of functionality into this app, there is quite a bit going on - I’m not sure how to duplicate the app and cut out a lot of it. If it’s of any use here is the image URL I am testing:

https://5ddn4vvordgezhta.anvil.app/version/dev/cd37bfa9a4f6b108//lm/table-media/a5b78e5cd2aceeb59661d7737ce0713d/1846366/Piccolo_logo_transparent.png?s=6fnaptnvkoa5tw6nzasgyedm

Can you see anything structurally problematic with that URL?

I get the same result as you that no app exists at that location, etc. Is that URL copied exactly from the bottom of the IDE?

Here is a clone to demonstrate how I set things up. Perhaps you could inspect it to see where the critical differences are compared to your app.

Clone:
https://anvil.works/build#clone:MPPC7W364ETBU2U3=MMGP6ZJYBNHQO3CAHQVX4YKU

My private app has the structure
https://{something}.anvil.app/_/private_api/{private key}/{path}

When published it has this structure (for example):
https://raw-decimal-chameleon.anvil.app/_/api/{path}

I’m sure that whatever is missing is just something trivial here. That is, I’m fairly certain that the URL is not correct but I’m not smart enough to know why yet.

Let me know if my examples correspond with how you are doing things generally.

unfortunately its not working as apparently the app is making use of paid plan features (although I am not sure where?)

I noticed you return ‘anvil.server.get_app_origin()’ and I am not sure how that would serve the image. The URL I pass through the API is one generated by calling image.url on the image file and storing it in a column in its table - would there be a reason why you can’t just go straight from the generated image.url?

Sorry, that was me testing things. Clone fixed. It simply returns the media object directly as described in the code snippet above.

I don’t think anything I’m doing requires a paid plan as far as I know.

regarding how you’ve set it up - its not quite the same. I believe you intend to display the image at the endpoint?

Whereas I am passing a list of jsons containing the image URLs as generated by the media_object.url method…

when using those URLs I am blocked… I don’t know if media_file.url gives a dud URL or something but I really don’t understand why I can’t just use the generated string to navigate to the file in a browser or in some other code.

Oh I see. Thanks for clarifying. Hmm, I’m sure there is a way.

Just tried again with an image.url generated today and I am met with an ‘internal server error’ - would really appreciate some clarity around how to use image.url properly for accessing files externally, I can’t just serve the actual file to an endpoint as the URLs need to be part of a larger consumed dictionary that the mobile app transforms and ingests.

Edit: Also thanks very much for all your efforts so far campopianoa!

1 Like

Okay, well using http requests I can render an image from a string returned from hitting an endpoint.

Clone for the app that is hosting images:
https://anvil.works/build#clone:JBZ2GLLBF4TWOJ4Z=K4MQJPIL3IE3FTGYW3XNVGE4

Relevant code:

# this returns the url through an endpoint as a string
@anvil.server.http_endpoint('/get_url')
def get_url():
  
  im=app_tables.my_images.get(name='cat1')['image']
  return im.url

Clone for the app that is getting images (from hitting the other app’s endpoint):
https://anvil.works/build#clone:5C6DM5UMF3POGDZ4=3O3U774QF6HSIZDPNM3EB6M6

Relevant code:

# this gets the url of the image by hitting the host's endpoint
@anvil.server.callable
def hit_endpoint():
  resp = anvil.http.request("{my_url}/get_url")

  return str(resp.get_bytes(),  'utf-8')

Perhaps there is a better way of doing this but I am making use of Anvil’s HTTP request to hit the endpoint. The endpoint returns a string to the calling app, and that string is the “source” for the Image component. Seems to work on my end.

1 Like

Ah, yes - the url property of Media objects is only valid:

  • If the Media object is permanent (eg a Data Table)
  • If you’re requesting it from the same browser session as the one that accessed the url property (if you’re requesting from outside the browser, each API request executes in a fresh session)

For more information, see https://anvil.works/docs/media#using-media-objects-directly

For what you’re doing, I would recommend creating an API endpoint that serves one image, and one that provides the JSON index. Something like this (I’m using row IDs to construct the URL for each image; you can use something else as long as it’s enough information to find the right image):

@anvil.server.http_endpoint('/all_images')
def get_images(**p):
  return [{'url': anvil.server.get_api_origin()+"/image/"+anvil.http.url_encode(row.get_id())}
          for row in app_tables.images.search()]

@anvil.server.http_endpoint('/image/:row_id')
def get_image(row_id, **p):
  row = app_tables.images.get_by_id(row_id)
  if row is not None:
    # Returning a Media object serves it as the HTTP response
    return row['image_column']
  else:
    return anvil.server.HttpResponse(status='404')

(Obviously, if you want those endpoints to be only accessible by certain users, you’ll want to do a bit of checking around “is this user allowed to access this row?”)

This kind of thing (making URLs that refer to particular database tables) is described in more detail in the guide @alcampopiano posted earlier:

How to build a simple HTTP API

1 Like

Aha I think I get it, you can’t just have a permalink link if you uploaded to a wordpress media library… you have to create a variable API endpoint for each image to be hosted - will try and forge ahead with what you’ve shown me and see if it can do what I need it to.

Cheers

1 Like

Exactly right! Permalinks are dangerous – not all content is public, so those URLs contain authentication tokens, and it’s a bad idea for authentication tokens to be valid forever. (It would mean that anyone who has been allowed access to this media once would be able to access it forever, which is fine for your application but could be dangerous for other people’s.)