我正在使用 Django 和 Django Rest Framework 开发一个项目,其中我必须在模型中保存图像。我使用 S3 存储桶作为存储设备。我可以上传图像并将其保存到模型中。
当获取响应(获取单个对象或对象数组)时,我得到带有两个
https://
的图像的 url。仅当我使用 AWS Ec2
上的 hosted的 Django 服务器时,才会发生这种情况。使用
localhost
时图像的url会正常返回,问题也出在静态文件上(但它们没有被使用,只有管理面板和rest-framework模板使用它)
这是回应。请注意 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"
}
]
这样的回应。再次注意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
请帮我解决这个问题!这是我第一次在生产中遇到此类错误...非常感谢您的时间和见解!!
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
这就是我所尝试的一切,它没有在本地主机上显示它,但问题仍然存在于生产服务器上。
我的前端托管在 vercel 上。虽然我不认为这是一个问题,因为响应本身有缺陷,所以前端没有对它做任何事情,我确信这一点!
你的静态网址应该是这样的
STATIC_URL = f'{AWS_CLOUDFRONT_DOMAIN}/{STATIC_LOCATION}/'