Получение двух "https://" в url изображения
Я разрабатываю проект с использованием Django и Django Rest Framework, в котором мне нужно сохранить изображение в модели. В качестве устройства хранения я использую S3 bucket. Мне удается загрузить изображение и сохранить его в модели.
Проблема
При получении ответа (либо при получении одного объекта, либо массива объектов) я получаю url изображения с двумя https://
. Это происходит только тогда, когда я использую сервер Django, который хостится на AWS Ec2
. При использовании localhost
url изображения возвращается нормально, проблема также возникает со статическими файлами (но они не используются, только админ-панель и шаблоны rest-framework)
Пример:
При вызове API с размещенного сервера
Это ответ. Обратите внимание на поле image.
[
{
"id": 5,
"image": "https://https://d2to87w45k79nd.cloudfront.net/media/testimonies/Myron/Section_2_img.png",
"name": "Myron",
"message": "Cool Website",
"position": "CEO",
"company": "ME Ltd."
},
{
"id": 6,
"image": "https://https://d2to87w45k79nd.cloudfront.net/media/testimonies/phoenix0347/Section_2_img.png",
"name": "phoenix0347",
"message": "askjn",
"position": "false",
"company": "false"
},
{
"id": 7,
"image": "https://https://d2to87w45k79nd.cloudfront.net/media/testimonies/Kushagra%20Gupta/Section_9.png",
"name": "Kushagra Gupta",
"message": "jksabdsadb",
"position": "1jb",
"company": "sajd"
},
{
"id": 8,
"image": "https://https://d2to87w45k79nd.cloudfront.net/media/testimonies/jksadb/hero_img.png",
"name": "jksadb",
"message": "akjsbasjdb",
"position": "213u3",
"company": "129ujieo2"
}
]
Тот же API при вызове с localhost дает
Ответ примерно такой. Снова обратите внимание на поле image!
[
{
"id": 5,
"image": "https://d2to87w45k79nd.cloudfront.net/media/testimonies/Myron/Section_2_img.png",
"name": "Myron",
"message": "Cool Website",
"position": "CEO",
"company": "ME Ltd."
},
{
"id": 6,
"image": "https://d2to87w45k79nd.cloudfront.net/media/testimonies/phoenix0347/Section_2_img.png",
"name": "phoenix0347",
"message": "askjn",
"position": "false",
"company": "false"
},
{
"id": 7,
"image": "https://d2to87w45k79nd.cloudfront.net/media/testimonies/Kushagra%20Gupta/Section_9.png",
"name": "Kushagra Gupta",
"message": "jksabdsadb",
"position": "1jb",
"company": "sajd"
},
{
"id": 8,
"image": "https://d2to87w45k79nd.cloudfront.net/media/testimonies/jksadb/hero_img.png",
"name": "jksadb",
"message": "akjsbasjdb",
"position": "213u3",
"company": "129ujieo2"
}
]
Я не знаю, в чем причина проблемы... Я использую пакеты boto3, django-storages python для достижения хранения в S3.
Я предоставляю свои settings.py
, storages.py
, views.py
, serializers.py
settings.py
# Static files (CSS, JavaScript, Images)
USE_S3 = not DEBUG
if USE_S3: # In Production Use CDN(Amazon CloudFront in this case)
# AWS Settings
AWS_ACCESS_KEY_ID = os.getenv('AWS_ACCESS_ID')
AWS_SECRET_ACCESS_KEY = os.getenv('AWS_SECRET_KEY')
AWS_STORAGE_BUCKET_NAME = os.getenv('AWS_S3_BUCKET_NAME')
AWS_DEFAULT_ACL = None
AWS_CLOUDFRONT_DOMAIN = os.getenv('AWS_CLOUDFRONT_DOMAIN')
AWS_S3_OBJECT_PARAMETERS = {'CacheControl': 'max-age=86400'}
# s3 static settings
STATIC_LOCATION = 'static'
STATIC_URL = f'https://{AWS_CLOUDFRONT_DOMAIN}/{STATIC_LOCATION}/'
STATICFILES_STORAGE = 'backend.storage_backends.StaticStorage'
# media settings
PUBLIC_MEDIA_LOCATION = 'media'
MEDIA_URL = f'{AWS_CLOUDFRONT_DOMAIN}/{PUBLIC_MEDIA_LOCATION}/'
DEFAULT_FILE_STORAGE = 'backend.storage_backends.MediaStorage'
else: # In Development use local storage (I can also use s3 if I wish to in development by setting DEBUG to false)
STATIC_URL = '/static/'
STATIC_ROOT = os.path.join(BASE_DIR, 'static/')
MEDIA_URL = '/media/'
MEDIA_ROOT = os.path.join(BASE_DIR, 'media/')
storages.py
from django.conf import settings
from storages.backends.s3boto3 import S3Boto3Storage
class StaticStorage(S3Boto3Storage):
location = settings.STATIC_LOCATION
default_acl = settings.AWS_DEFAULT_ACL
def __init__(self, **kwargs):
kwargs['custom_domain'] = settings.AWS_CLOUDFRONT_DOMAIN
super(StaticStorage, self).__init__(**kwargs)
class MediaStorage(S3Boto3Storage):
location = settings.PUBLIC_MEDIA_LOCATION
default_acl = settings.AWS_DEFAULT_ACL
file_overwrite = False
def __init__(self, **kwargs):
kwargs['custom_domain'] = settings.AWS_CLOUDFRONT_DOMAIN
super(MediaStorage, self).__init__(**kwargs)
serializer.py
from rest_framework import serializers
from .models import Testimonies
class TestimonySerializer(serializers.ModelSerializer):
image = serializers.ImageField()
@staticmethod
def get_image(instance):
try:
print(instance.image.url)
return instance.image.url
except Exception as e:
print(e)
return ''
class Meta:
model = Testimonies
fields = '__all__'
views.py
from rest_framework import status
from rest_framework.generics import GenericAPIView
from rest_framework.parsers import MultiPartParser, FormParser
from rest_framework.response import Response
from .serializers import TestimonySerializer
from .models import Testimonies
class Testimony(GenericAPIView):
serializer_class = TestimonySerializer
queryset = Testimonies.objects.all()
lookup_field = 'id'
parser_classes = [MultiPartParser, FormParser]
def get(self, req, *args, **kwargs):
# Get single Testimony
if 'id' in kwargs:
testimony = self.get_object()
serializer = self.get_serializer(testimony)
return Response(serializer.data, status=status.HTTP_200_OK)
# Get All Testimonies
testimonies = self.get_queryset()
serializer = self.get_serializer(testimonies, many=True)
return Response(serializer.data, status=status.HTTP_200_OK)
def post(self, req, **kwargs):
data = self.request.data
try:
serializer = self.get_serializer(data=data)
serializer.is_valid(raise_exception=True)
serializer.save()
res = {
"message": "Testimony Added Successfully",
"success": True
}
return Response(res, status=status.HTTP_201_CREATED)
except Exception as e:
print(e)
res = {
"message": "Something went wrong",
"success": False,
"error": str(e),
}
return Response(res, status=status.HTTP_400_BAD_REQUEST)
models.py
from django.db import models
def set_image_path(instance, filename):
return f"testimonies/{instance.name}/{filename}"
class Testimonies(models.Model):
name = models.CharField(max_length=200)
message = models.TextField()
image = models.ImageField(upload_to=set_image_path)
position = models.CharField(max_length=200)
company = models.CharField(max_length=300)
def __str__(self):
return self.name
Редактирование: Также добавлены настройки Cloudfront
Пожалуйста, помогите!!! Это первый раз, когда я столкнулся с такой ошибкой в производстве... Спасибо большое за ваше время и понимание!!!
- Я попробовал использовать
SerializerMethodField
для поля изображения вserializers.py
, но при этом мне не удалось загрузить изображение. - Я попробовал изменить
MEDIA_URL
вsettings.py
.
- Я попробовал заглянуть в код библиотеки, там я увидел, что она действительно добавляет
https://
, но она добавляет его передcustom_domain
, который я установил вstorages.py
наCLOUDFRONT_DOMAIN
. Я вставлю этот конкретный код из библиотеки сюда.
def url(self, name, parameters=None, expire=None, http_method=None):
# Preserve the trailing slash after normalizing the path.
name = self._normalize_name(clean_name(name))
params = parameters.copy() if parameters else {}
if expire is None:
expire = self.querystring_expire
if self.custom_domain:
url = "{}//{}/{}{}".format(
self.url_protocol,
self.custom_domain,
filepath_to_uri(name),
"?{}".format(urlencode(params)) if params else "",
)
if self.querystring_auth and self.cloudfront_signer:
expiration = datetime.utcnow() + timedelta(seconds=expire)
return self.cloudfront_signer.generate_presigned_url(
url, date_less_than=expiration
)
return url
Это все, что я пробовал, с этим он не показывал его на localhost, но проблема все еще сохранялась на рабочем сервере.
Мой фронтенд размещен на vercel. Хотя я не думаю, что это проблема, потому что сам ответ имеет дефект, так что фронтенд ничего с ним не делает, в этом я уверен!
Ваш статический url должен выглядеть следующим образом
STATIC_URL = f'{AWS_CLOUDFRONT_DOMAIN}/{STATIC_LOCATION}/'