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::
- Proxy view which takes path to image (Amazon s3 object key) and generate presigned url.
- 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.