Search code examples
djangoazure-storage

Serving images in Django that aren't part of static


I'm looking for a pattern for rendering images that aren't in static, but all of the documentation seems to require that they be in static. In my case I'd like to get the image from an Azure storage blob, but the file can't be hosted publicly.

My ideal pattern is: view.py

def view_my_scene(request):
    scene_id = request.GET.get('scene_id')
    context={'scene_id': scene_id}
    my_image = my_other_function_that_gets_image(scene_id)
    return render(request, "my_scene_with_an_image.html", context, my_image)

And then be able to use that image, but I can't find a pattern like that. Is there a way to render images on the fly? How about if it was an image I just created?


Solution

  • I agree with Mohamed Azarudeen Z's comment. If you are using Django, you have the option to create images dynamically and send them as a response. One way to do this is by using the Python Imaging Library (Pillow) to generate the image in memory and then serve it as a response.

    Here is my views.py:

    from django.shortcuts import render
    from django.http import HttpResponse
    from azure.storage.blob import BlobServiceClient
    from io import BytesIO
    from PIL import Image
    import base64
    
    AZURE_STORAGE_CONNECTION_STRING = "xxxxx"
    CONTAINER_NAME = "xxx"
    
    def get_image_from_azure_storage(scene_id):
        blob_service_client = BlobServiceClient.from_connection_string(AZURE_STORAGE_CONNECTION_STRING)
        container_client = blob_service_client.get_container_client(CONTAINER_NAME)
        blob_name = f"scene_{scene_id}.png"
        
        blob_client = container_client.get_blob_client(blob_name)
        blob_data = blob_client.download_blob()
        
        # Read the blob data into a PIL Image
        image_data = BytesIO(blob_data.readall())
        image = Image.open(image_data)
        
        return image
    
    def my_other_function_that_gets_image(scene_id):
        try:
            image = get_image_from_azure_storage(scene_id)
        except Exception as e:
            print(f"Error retrieving image for scene_id {scene_id}: {e}")
            image = Image.new('RGB', (100, 100), color='red')
        image_bytes = BytesIO()
        image.save(image_bytes, format='PNG')
        image_base64 = base64.b64encode(image_bytes.getvalue()).decode('utf-8')
    
        return f"data:image/png;base64,{image_base64}"
    
    def view_my_scene(request):
        scene_id = request.GET.get('scene_id')
        my_image_base64 = my_other_function_that_gets_image(scene_id)    
        context = {'scene_id': scene_id, 'my_image': my_image_base64}
        return render(request, "my_scene_with_an_image.html", context)
    
    def download_my_scene(request):
        scene_id = request.GET.get('scene_id')
        my_image_base64 = my_other_function_that_gets_image(scene_id)
        image_base64 = my_image_base64.split(',')[1]
        image_bytes = base64.b64decode(image_base64)
        response = HttpResponse(image_bytes, content_type='image/png')
        response['Content-Disposition'] = f'attachment; filename=scene_{scene_id}.png'
    
        return response
    

    The above code retrieves an image from Azure Storage and converts it to base64 format. It defines functions to retrieve the image and render it in an HTML page or download it as a PNG file. The PIL library is used for image processing and manipulation.

    My template:

    <!DOCTYPE html>
    <html>
    <head>
        <title>My Scene with an Image</title>
    </head>
    <body>
        <h1>Scene ID: {{ scene_id }}</h1>
        <img src="{{ my_image }}" alt="My Scene Image">
        <a href="{% url 'download_my_scene' %}?scene_id={{ scene_id }}" download="scene_{{ scene_id }}.png">
            Download Image
        </a>
    </body>
    </html>
    

    In my environment, I stored images with scene_123.png and scene_234.png.

    When I copied and pasted the URL in the browser with http://127.0.0.1:8000/my_scene/?scene_id=234

    Output: enter image description here

    You can also download the image by clicking the Download Image link.