Issue
I'm trying to add an ImageField
to a model called LucyGuide
which will save user-uploaded images to S3 using django-storages
. Here is the (simplified) model:
class LucyGuide(TimeStampedModel):
headshot = models.ImageField(upload_to='uploads/', null=True)
bio = models.TextField(blank=True)
I've added the following to my settings.py
:
# Use Boto3 backend to interact with Amazon's S3
DEFAULT_FILE_STORAGE = 'storages.backends.s3boto3.S3Boto3Storage'
# Amazon S3 credentials (for django-storages)
AWS_ACCESS_KEY_ID = os.getenv('AWS_ACCESS_KEY_ID', default='')
AWS_SECRET_ACCESS_KEY = os.getenv('AWS_SECRET_ACCESS_KEY', default='')
AWS_STORAGE_BUCKET_NAME = os.getenv('AWS_STORAGE_BUCKET_NAME', default='')
AWS_S3_REGION_NAME = os.getenv('AWS_S3_REGION_NAME')
AWS_S3_OBJECT_PARAMETERS = {
'CacheControl': 'max-age=86400',
}
# Use v4 of the signing protocol (recommended for all new projects)
AWS_S3_SIGNATURE_VERSION = 's3v4'
where the actual keys are read from a .env
file (using a mechanism similar to django-decouple
).
To try this out, I uploaded a random picture for a LucyGuide
in Django's admin UI:
In the shell, I'm able to access the url
attribute of the guide's headshot
field, which is indeed a link to an AWS bucket:
In [6]: guide = LucyGuide.objects.filter(bio__startswith="Kristen").first()
In [7]: guide
Out[7]: <LucyGuide: Kristen Hoover>
In [8]: guide.headshot
Out[8]: <ImageFieldFile: uploads/320px-Waaah.jpg>
In [9]: guide.headshot.url
Out[9]: 'https://lucy-prod.s3.amazonaws.com/uploads/320px-Waaah.jpg?X-Amz-Algorithm=AWS4-HMAC-SHA256&X-Amz-Credential=AKIAIMC2A%2F20180327%2Fus-east-1%2Fs3%2Faws4_request&X-Amz-Date=20180327T200248Z&X-Amz-Expires=3600&X-Amz-SignedHeaders=host&X-Amz-Signature=ae75dbdd75d13020113c12ef2d655e3'
(where I've deleted parts of the URL). The problem is that when I try to go to this URL in the browser, I get a "This XML file does not appear to have any style information associated with it" error:
<Error>
<Code>AuthorizationQueryParametersError</Code>
<Message>
Error parsing the X-Amz-Credential parameter; the region 'us-east-1' is wrong; expecting 'us-west-1'
</Message>
<Region>us-west-1</Region>
<RequestId>1E053D94011E400F</RequestId>
<HostId>
jbkRHVj2y6ygppTsAo2+uOXgby0ok0mbsFsRogKqbu9jPMb+9eGe24nJv441vip3WpmwpFqlsYg=
</HostId>
</Error>
I've already tried to solve this by adding the AWS_S3_REGION_NAME
setting (cf. http://django-storages.readthedocs.io/en/latest/backends/amazon-S3.html). The region us-west-1
indicated by the error message seems to be the correct one, since the bucket is set up in "US West (N. California)":
All in all, I don't see why this error is occurring despite setting the correct AWS_S3_REGION_NAME
. How can I fix this error?
Update
If I inspect the object in the bucket, I see that it has a "Link" which is much simpler than the one generated by the url
property of the headshot
field:
I'm thinking of just hard-coding the 'base URL' shown here into the API endpoint I'm trying to build that retrieves image URLs, but this doesn't seem like an elegant solution. Any better ideas?
Solution
By reading (and setting a breakpoint in) the django-storages
source code, I was able to solve the problem by adding the setting
AWS_QUERYSTRING_AUTH = False
which changes it from its default value of True
. Now the url
attribute generates a URL without an authentication querystring:
In [2]: guide = LucyGuide.objects.filter(bio__startswith='Darrell').first()
In [3]: guide.headshot
Out[3]: <ImageFieldFile: uploads/Waaah.jpg>
In [4]: guide.headshot.url
Out[4]: 'https://lucy-prod.s3.amazonaws.com/uploads/Waaah.jpg'
Answered By - Kurt Peek
0 comments:
Post a Comment
Note: Only a member of this blog may post a comment.