'Django ckeditor upload image link expires after some hours

I am using Django-storages for storing static content in the cloud and CKEditor for a text editor and used a RichTextUploadingField field. The problem is when I upload an image via CKEditor, the image link expires after some hours. The below image for reference.

enter image description here

And the field in the application.

bio = RichTextUploadingField(blank=True, null=True)

If I upload an image via ImageField, the link never expires its works fine. the problem occurs only in the CKEditor image uploader.

Any idea why the link expires?



Solution 1:[1]

The problem is that links with authorization are hard coded in html string. Default settings time expired is 1 hour. so after one hour from editing html in editor they are expired.

I found many suggestion on forums and on GitHub issue to turn of s3 link query authorization and make storage public. Clever suggestions.... customer would be happy.

My walk around::

  1. Proxy view which takes path to image (Amazon s3 object key) and generate presigned url.
  2. On model save i take html value and using regex replace img path to s3 on my proxy path.

Create path:

#url.py
from .s3proxy import s3proxy
url += [
    ...
    path('s3image', s3proxy),
    ...
]

Build proxy view and for convinced i placed transforming url function in this same file.

# s3proxy.py
from django.http import HttpResponseRedirect
import boto3
import os
import re

AWS_ACCESS_KEY_ID = os.getenv('AWS_ACCESS_KEY_ID')
AWS_SECRET_ACCESS_KEY = os.getenv('AWS_SECRET_ACCESS_KEY')
AWS_STORAGE_BUCKET_NAME = os.getenv('AWS_STORAGE_BUCKET_NAME')
AWS_URL = os.getenv('AWS_URL')
AWS_S3_REGION_NAME = 'eu-central-1'

def s3proxy(req):
    path = req.GET.get('p', None)

    if not path:
        return HttpResponse(status=404)

    s3_client = boto3.client('s3',
        aws_access_key_id= AWS_ACCESS_KEY_ID,
        aws_secret_access_key=AWS_SECRET_ACCESS_KEY,
        region_name=AWS_S3_REGION_NAME
    )

    response = s3_client.generate_presigned_url(
        ClientMethod = 'get_object',
        Params={
            'Bucket': AWS_STORAGE_BUCKET_NAME,
            'Key': path
        }, ExpiresIn=172800
    )

    return HttpResponseRedirect(response)

def transformImgUrl(html):
    img_path = "(?:"+AWS_URL+")(.*\.(?:png|jpg|jpeg|gif))"
    pattern = "(<img.*?src=\")"+img_path+".*\""
    transformed_html = re.sub(pattern, "\\1/s3image?p=\\2\"", html)
    return transformed_html

And on save method in model:

# model file
class MyModel(models.Model):
    html = models.TextField(
        verbose_name = 'Html value',
        default = ''
    )
    def save(self):
        self.html= transformImgUrl(self.html)
        super(MyModel, self).save()

Now all images path after saving point to my proxy path which generating redirect to amazon s3. 

so it take from ckeditor html content:

"""
https://fasdfasdf.s3.amazonaws.com/media/upload/fake-img.png?X-Amz-Algorithm=AWS4-HMAC-SHA256&X-Amz-Credential=AKIA4L6S3asdfasdf210316%2Feu-central-1%2Fs3%2Faws4_request&X-Amz-Date=202103145654094143Z&X-Amz-Expires=172800&X-Amz-SignedHeaders=host&X-Amz-Signature=2494010ad11asdfasdfsfa36a70ab3dab4506b70eda7e4366fcd9b3d72ac
"""

and make this url in img src:

"/s3image?p=media/upload/fake-img.png"

now when brosswer call this url then it generate new amazon s3 resource url with auth query and redirect call to it.

In my version i set expired time on 2 days (172800s) because i have in plan set one day cache. But be careful long expired time are not valid. I don't check exactly, but you can't set expired time on year event month limit is approx 10 days maybe 14 someting like this. 

The only disadvantage is accessing time because of proxy route and additional calls to create presigned url. This can be cached in redis or something but not now in my case.

Not perfect but for sure better this than make storage public.

Solution 2:[2]

This issue was solved for me by making my s3 bucket public and following this post.

After making your S3 bucket public add this line to your config/settings.py file:

AWS_QUERYSTRING_AUTH = False

If you check the HTML of your app you will find that ckeditor includes an expiration time for the image URL. Adding this line seems to eliminate everything after the "?" on these URLs when they are uploaded.

Before: https://your-bucket-name.s3.amazonaws.com/your-path/your_image.png?AWSAccessKeyId=#########;Signature=#########;**Expires=1651064798**

After: https://your-bucket-name.s3.amazonaws.com/your-path/your_image.png

If you've already done a lot of work and uploaded a ton of images via CKEditor then don't worry, you don't need to re-upload them all after adding this line of code to your settings file! Just head back into the RichTextEditor, select the "Source" option to view the source code of the text field, and delete everything after the "?" that tails the end of the image URLs you uploaded (the AWSAccessKeyID, Signature, and Expires string).

Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source
Solution 1
Solution 2 kidbilly