2

I have a django project that uploads files to a AWS S3 bucket. The uploaded files and the static files are shown correctly if they are rendered in the template using the {% static %} tag for example. However, when I want to access the image in a javascript code using model_object.image.url, the image doesn't show up. I inspected the code, took the url that was rendered in the js code and pasted it on the browser, it gives me this error:

<Error>
<Code>AccessDenied</Code>
<Message>Query-string authentication requires the Signature, Expires and AWSAccessKeyId parameters</Message>
<RequestId>xxxx</RequestId><HostId>xxx</HostId>
</Error>

Some more information that might be useful:

The user uploads an image, then he can crop the image. I use Croppie for this, it is a js library that uses the url of the image like this:

$('.div').croppie({
    url: '{{ model_object.image.url }}',
});

Everything works locally. It is a problem with AWS I clearly don't understand.

Following solarissmoke comment, I added this bucket policy to my bucket:

{
    "Version": "2012-10-17",
    "Id": "Policy1468082822770",
    "Statement": [
        {
            "Sid": "Stmt1468082812651",
            "Effect": "Allow",
            "Principal": "*",
            "Action": "s3:GetObject",
            "Resource": "arn:aws:s3:::criptolibertad/*"
        }
    ]
}

However, it still doesn't work. I noticed the url rendered in the template that doesn't work looks like this:

https://criptolibertad.s3.amazonaws.com/Django/0_squashmigrations.jpeg?Signature=HFDdOYvrfqz5DG...

If I open the resource directly from my bucket, the url looks like this:

https://s3-us-west-2.amazonaws.com/criptolibertad/Django/0_squash+migrations.jpeg?X-Amz-Date=201607...

I also right clicked on the folder and selected Make Public just in case.

Any advice will help.

isherwood
  • 58,414
  • 16
  • 114
  • 157
Alejandro Veintimilla
  • 10,743
  • 23
  • 91
  • 180
  • 1
    Looks like your S3 bucket/objects are not public, which they need to be to access them without authentication. See [this question](http://stackoverflow.com/questions/19176926/how-to-make-all-objects-in-aws-s3-bucket-public-by-default) for some ideas. – solarissmoke Jul 09 '16 at 05:49
  • @solarissmoke Thanks for the comment, I added a policy now. But it still doesn't work. – Alejandro Veintimilla Jul 09 '16 at 17:11

2 Answers2

1

First of all, the links above point to different paths, one is the file Django/0_squash+migrations.jpeg the other is Django/0_squashmigrations.jpeg. The latter is the one not working for me and it seems it's missing a + character (space?); I cannot access that file at all (I get an access error). I'm going to assume that's just a typo on your part.

Being familiar with your code (let me know if you want the link redacted), I have simulated the image upload, and even copied your policy over to my bucket.

What I noticed is that my model_objecct.image.url differs from yours in that it includes the AWSAccessKeyId like so:

> fs = FeralSpirit.objects.all()[1]
> print(fs.imagen.url)
https://so38134984.s3.amazonaws.com/OrillaLibertaria/Users/pavel/dev/temp/so38134984/rainbow_dash2.png?Signature=****&Expires=*****&AWSAccessKeyId=*******

The link in your example only includes the signature, thus the error message you see is very appropriate.

In fact, we can access the image above without any query strings just ok.

If you're trying to remove the query string parameters, since this will be a publicly accessible bucket, try adding AWS_QUERYSTRING_AUTH = False to your Django settings. This should generate the url without any query string parameters:

> fs = FeralSpirit.objects.all()[1]
> fs.imagen.url
'https://so38134984.s3.amazonaws.com/OrillaLibertaria/Users/pavel/dev/temp/so38134984/rainbow_dash2.png'

Which is still publicly accessible of course.

tutuDajuju
  • 10,307
  • 6
  • 65
  • 88
  • Thanks for your answer man, I just edited and actualized the question. – Alejandro Veintimilla Jul 13 '16 at 20:06
  • Your question now has little to do with S3, I suggest you edit back the question, and ask a new one – tutuDajuju Jul 14 '16 at 05:19
  • To start you off, inspect the chrome developer tools log for any errors – tutuDajuju Jul 14 '16 at 08:08
  • @tutuDajaju I added the error to the question... I ran out of ideas. – Alejandro Veintimilla Jul 14 '16 at 14:52
  • Googling "Image blocked from loading by Cross-Origin Resource Sharing policy" leads to this [SO question](http://stackoverflow.com/q/25577981/484127) and [AWS S3 documentation](http://docs.aws.amazon.com/AmazonS3/latest/dev/cors.html) for info. I feel bad polluting the question with a CORS issue, therefor I have reverted this question to it's original form to help others with a similar problem. If you still have an issue with CORS, open a separate question ([use your latest revision source](http://stackoverflow.com/revisions/0ebc65d4-90b7-4c30-acf5-e298a0cde8c7/view-source)). – tutuDajuju Jul 14 '16 at 18:33
  • 1
    Ok @tutuDajuju I agree. However, given that I've tried everything that came to my mind, even asked [this](http://stackoverflow.com/questions/38365182/image-saved-con-amazon-s3-bucket-not-showing-on-html5-canvas-crossorigin-issue) new question with no answer till now, I decided to hack my way out of this in a weird way. Thanks for all your attention. GL HF. – Alejandro Veintimilla Jul 14 '16 at 21:51
  • Sure thing I've answered that question @alejoss . Please remember to mark answers as accepted if they helped you resolve the issue in the question. – tutuDajuju Jul 15 '16 at 07:37
0

Ok, thanks to tutuDajajo and his answer in this question I asked when I narrowed down the issue, I found the problem and the solution:

Croppie.js was trying to load the image into an HTML5 canvas. Croppie.js automatically added crossorigin="anonymous" to the image. But the image was missing the correct CORS headers and the canvas was therefore "tainted".

I found out that changing Croppie.js source and removing crossorigin="anonymous" worked partially: The image was loaded correctly into the canvas, but it was impossible to export it and get the base64 image I needed to send to the server.

The real solution was to change my bucket CORS configuration, from this:

<AllowedHeader>Authorization</AllowedHeader>

to this:

<AllowedHeader>*</AllowedHeader>

BTW. AWS documentation is not clear about the AllowedHeader options.

Community
  • 1
  • 1
Alejandro Veintimilla
  • 10,743
  • 23
  • 91
  • 180