Получение двух "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 settings of AWS

Пожалуйста, помогите!!! Это первый раз, когда я столкнулся с такой ошибкой в производстве... Спасибо большое за ваше время и понимание!!!

  1. Я попробовал использовать SerializerMethodField для поля изображения в serializers.py, но при этом мне не удалось загрузить изображение.
  2. Я попробовал изменить MEDIA_URL в settings.py
  3. .
  4. Я попробовал заглянуть в код библиотеки, там я увидел, что она действительно добавляет 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}/'
Вернуться на верх