-
Notifications
You must be signed in to change notification settings - Fork 1.8k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
boto3 lib of python, s3 image upload using presigned url with content type #1149
Comments
How are you trying to upload the image? Could you show a sample code snippet or command you ran to get the |
<html>
<head>
<script src="http://code.jquery.com/jquery-latest.min.js"></script>
<script type="text/javascript">
// Remember to include jQuery somewhere.
// Remember to include jQuery somewhere.
$(document).ready(function(){
prisigned_url=[REDACTED];
$(function() {
$('#theForm').on('submit', sendFile);
});
function sendFile(e) {
e.preventDefault();
// get the reference to the actual file in the input
var theFormFile = $('#theFile').get()[0].files[0];
$.ajax({
type: 'PUT',
url:prisigned_url, //server will send presigned url to upload image expires in 3600
// Content type must much with the parameter you signed your URL with
//contentType: 'binary/octet-stream',
// this flag is important, if not set, it will try to send data as a form
//ContentType: 'image/jpg',
processData: false,
// the actual file is sent raw
data: theFormFile
})
.success(function(file,response) {
console.log("file=>",file);
console.log("response=>",response);
alert('File uploaded');
})
.error(function() {
alert('File NOT uploaded');
console.log( arguments);
});
return false;
}
});
</script>
</head>
<body>
<form id="theForm" method="POST" enctype="multipart/form-data" >
<input id="theFile" name="file" type="file"/>
<button id="theButton" type="submit">send 1</button>
</form>
</body>
</html> |
above code is my javascript ajax call and html. where I am using preassigned URL. |
You need to make sure that bucket's CORS config is set to accept the When you make the Here is a cors config document that works for me with your script:
All I did was add that last That config worked for the following two scripts to presign the url and upload the image: Generate the presigned url: import boto3
import botocore
s3_con = boto3.client('s3')
url=s3_con.generate_presigned_url('put_object',
Params={'Bucket': 'bucket_name',
'Key':'img.jpg',
'ContentType': 'image/jpg'
},
ExpiresIn=600)
print(url) Ajax call to upload: presigned_url = "...";
function sendFile(e) {
e.preventDefault();
// get the reference to the actual file in the input
var theFormFile = $('#theFile').get()[0].files[0];
$.ajax({
type: 'PUT',
url:presigned_url,
contentType: 'image/jpg',
processData: false,
data: theFormFile
}).success(function(file,response) {
console.log("file=>",file);
console.log("response=>",response);
alert('File uploaded');
}).error(function() {
alert('File NOT uploaded');
console.log( arguments);
});
} |
Thank you I will try and come back to you.
…On Tue, Jun 27, 2017 at 4:19 AM, John Carlyle ***@***.***> wrote:
You need to make sure that bucket's CORS config is set to accept the
content-type header.
When you make the PUT request ajax makes a preflight OPTIONS request to
see if the request it is about to make is allowed. S3 will check the
preflight headers against that buckets cors config object to ensure
everything is allowed. Since by default the Content-Type header is not in
the list of allowed headers the preflight request will fail and ajax will
not make the PUT request.
Here is a cors config document that works for me with your script:
<?xml version="1.0" encoding="UTF-8"?>
<CORSConfiguration xmlns="http://s3.amazonaws.com/doc/2006-03-01/">
<CORSRule>
<AllowedOrigin>*</AllowedOrigin>
<AllowedMethod>GET</AllowedMethod>
<AllowedMethod>PUT</AllowedMethod>
<MaxAgeSeconds>3000</MaxAgeSeconds>
<ExposeHeader>GET</ExposeHeader>
<ExposeHeader>PUT</ExposeHeader>
<AllowedHeader>Authorization</AllowedHeader>
<AllowedHeader>Content-Type</AllowedHeader>
</CORSRule>
</CORSConfiguration>
All I did was add that last <AllowedHeader>Content-Type</AllowedHeader>
line to the default one.
That config worked for the following two scripts to presign the url and
upload the image:
Generate the presigned url:
import boto3import botocore
s3_con = boto3.client('s3')
url=s3_con.generate_presigned_url('put_object',
Params={'Bucket': 'bucket_name',
'Key':'img.jpg',
'ContentType': 'image/jpg'
},
ExpiresIn=600)print(url)
Ajax call to upload:
presigned_url = "...";
function sendFile(e) {
e.preventDefault();
// get the reference to the actual file in the input
var theFormFile = $('#theFile').get()[0].files[0];
$.ajax({
type: 'PUT',
url:presigned_url,
contentType: 'image/jpg',
processData: false,
data: theFormFile
}).success(function(file,response) {
console.log("file=>",file);
console.log("response=>",response);
alert('File uploaded');
}).error(function() {
alert('File NOT uploaded');
console.log( arguments);
});
}
—
You are receiving this because you authored the thread.
Reply to this email directly, view it on GitHub
<#1149 (comment)>, or mute
the thread
<https://github.com/notifications/unsubscribe-auth/AHJF5i98evU6cezR5zg1zvS0dkscAVbfks5sIDWBgaJpZM4OCm3A>
.
|
Hi,
Still I am getting same error.
I have setup following things
<?xml version="1.0" encoding="UTF-8"?>
<CORSConfiguration xmlns="http://s3.amazonaws.com/doc/2006-03-01/">
<CORSRule>
<AllowedOrigin>*</AllowedOrigin>
<AllowedMethod>GET</AllowedMethod>
<AllowedMethod>PUT</AllowedMethod>
<MaxAgeSeconds>3000</MaxAgeSeconds>
<ExposeHeader>GET</ExposeHeader>
<ExposeHeader>PUT</ExposeHeader>
<AllowedHeader>Authorization</AllowedHeader>
<AllowedHeader>Content-Type</AllowedHeader>
</CORSRule>
</CORSConfiguration>
Policy
{
"Version": "2012-10-17",
"Id": "Policy1497602910515",
"Statement": [
{
"Sid": "Stmt1497602844610",
"Effect": "Allow",
"Principal": "*",
"Action": "s3:GetObject",
"Resource": "arn:aws:s3:::piruby-image-test/*"
},
{
"Sid": "Stmt1497602909333",
"Effect": "Allow",
"Principal": {
"AWS": "arn:aws:iam::826580139798:user/shashank"
},
"Action": [
"s3:DeleteObject",
"s3:GetObject",
"s3:PutObject",
"s3:PutObjectAcl",
"s3:PutObjectTagging"
],
"Resource": "arn:aws:s3:::piruby-image-test/*"
}
]
}
s3_con =
boto3.client('s3',aws_access_key_id='xxxxx',aws_secret_access_key='xxxxxx',config=Config(signature_version='s3v4'),region_name='xxxx')
url=s3_con.generate_presigned_url('put_object',
Params={'Bucket':'bucket_name',
'Key':key,'ContentType':'image/jpg'},ExpiresIn=3600, HttpMethod='PUT')
<html>
<head>
<script src="http://code.jquery.com/jquery-latest.min.js"></script>
<script type="text/javascript">
// Remember to include jQuery somewhere.
// Remember to include jQuery somewhere.
$(document).ready(function(){
prisigned_url="
https://s3.ap-south-1.amazonaws.com/piruby-image-test/ashish.g%40pacewisdom.com/20170630114210.jpg?X-Amz-Algorithm=AWS4-HMAC-SHA256&X-Amz-Credential=AKIAJZ5CFUPGRTXVSYWA%2F20170630%2Fap-south-1%2Fs3%2Faws4_request&X-Amz-Date=20170630T114210Z&X-Amz-Expires=3600&X-Amz-SignedHeaders=content-type%3Bhost&X-Amz-Signature=b385584eafdb4472d9f06ea0a5daf1902b06b4fbd05c7cb0e48e03b9b07ba518
";
$(function() {
$('#theForm').on('submit', sendFile);
});
function sendFile(e) {
e.preventDefault();
// get the reference to the actual file in the input
var theFormFile = $('#theFile').get()[0].files[0];
$.ajax({
type: 'PUT',
url:prisigned_url, //server will send presigned url to upload
image expires in 3600
// Content type must much with the parameter you signed your URL
with
//x-amz-meta-contentType: 'binary/octet-stream',
// this flag is important, if not set, it will try to send data
as a form
// x-amz-meta-ContentType: 'image/jpeg',
processData: false,
// the actual file is sent raw
data: theFormFile
})
.success(function(file,response) {
console.log("file=>",file);
console.log("response=>",response);
alert('File uploaded');
})
.error(function() {
alert('File NOT uploaded');
console.log( arguments);
});
return false;
}
});
</script>
</head>
<body>
<form id="theForm" method="POST" enctype="multipart/form-data" >
<input id="theFile" name="file" type="file"/>
<button id="theButton" type="submit">send 1</button>
</form>
</body>
</html>
Please let me know the issue with my code.
On Wed, Jun 28, 2017 at 1:10 PM, Ashish Gupta <ashish.2007g@gmail.com>
wrote:
… Thank you I will try and come back to you.
On Tue, Jun 27, 2017 at 4:19 AM, John Carlyle ***@***.***>
wrote:
> You need to make sure that bucket's CORS config is set to accept the
> content-type header.
>
> When you make the PUT request ajax makes a preflight OPTIONS request to
> see if the request it is about to make is allowed. S3 will check the
> preflight headers against that buckets cors config object to ensure
> everything is allowed. Since by default the Content-Type header is not
> in the list of allowed headers the preflight request will fail and ajax
> will not make the PUT request.
>
> Here is a cors config document that works for me with your script:
>
> <?xml version="1.0" encoding="UTF-8"?>
> <CORSConfiguration xmlns="http://s3.amazonaws.com/doc/2006-03-01/">
> <CORSRule>
> <AllowedOrigin>*</AllowedOrigin>
> <AllowedMethod>GET</AllowedMethod>
> <AllowedMethod>PUT</AllowedMethod>
> <MaxAgeSeconds>3000</MaxAgeSeconds>
> <ExposeHeader>GET</ExposeHeader>
> <ExposeHeader>PUT</ExposeHeader>
> <AllowedHeader>Authorization</AllowedHeader>
> <AllowedHeader>Content-Type</AllowedHeader>
> </CORSRule>
> </CORSConfiguration>
>
> All I did was add that last <AllowedHeader>Content-Type</AllowedHeader>
> line to the default one.
>
> That config worked for the following two scripts to presign the url and
> upload the image:
>
> Generate the presigned url:
>
> import boto3import botocore
>
>
> s3_con = boto3.client('s3')
> url=s3_con.generate_presigned_url('put_object',
> Params={'Bucket': 'bucket_name',
> 'Key':'img.jpg',
> 'ContentType': 'image/jpg'
> },
> ExpiresIn=600)print(url)
>
> Ajax call to upload:
>
> presigned_url = "...";
> function sendFile(e) {
> e.preventDefault();
>
> // get the reference to the actual file in the input
> var theFormFile = $('#theFile').get()[0].files[0];
>
> $.ajax({
> type: 'PUT',
> url:presigned_url,
> contentType: 'image/jpg',
> processData: false,
> data: theFormFile
> }).success(function(file,response) {
> console.log("file=>",file);
> console.log("response=>",response);
>
> alert('File uploaded');
> }).error(function() {
> alert('File NOT uploaded');
> console.log( arguments);
> });
> }
>
> —
> You are receiving this because you authored the thread.
> Reply to this email directly, view it on GitHub
> <#1149 (comment)>, or mute
> the thread
> <https://github.com/notifications/unsubscribe-auth/AHJF5i98evU6cezR5zg1zvS0dkscAVbfks5sIDWBgaJpZM4OCm3A>
> .
>
|
I don't know what the particular issue with your code is. I would suggest you open a question on stackoverflow about it. We don't have the bandwidth here to deal with questions. |
You need to make sure the file is already uploaded before you ask for the presigned url. |
Hi Jack,
I found a solution, After uploading image to S3, I am changing the content
type of image.
…On Thu, Mar 22, 2018 at 6:33 PM, Jack Urban ***@***.***> wrote:
I'm guessing your webapp uses django or flask. Make sure that your webapp
has the appropriate CORS set on the webapp end, in addition to AWS end.
I do not believe you need the file to exist on s3 before generating an
upload url.
—
You are receiving this because you authored the thread.
Reply to this email directly, view it on GitHub
<#1149 (comment)>, or mute
the thread
<https://github.com/notifications/unsubscribe-auth/AHJF5gcVb_lHUDRucD-Bh8ECrXeJ0RPMks5tg6EtgaJpZM4OCm3A>
.
|
Hi @ashishgupta2014 I am experiencing exactly the same issue as you. Is there any chance you can elaborate on how you fixed it please? Thanks :) |
@Owen045 I came to this thread with the same issue, fixed it for myself by setting up the boto3 client similar to what OP had above:
boto3 will look for the other stuff in environment variables, but not signature version or region name. In my case, those are in django settings. I spent 12 hours to find this fix. To make this thread more useful for people troubleshooting similar issues - is there a way to confirm that boto3 is sending everything you expect? I know you could do the following to see the exact request being sent to amazon, but the logs still don't have an explicit declaration of what region it's redirecting to. |
@joshkpeterson Thanks for the help Josh, although I had been hardcoding in my region for testing purposes atm. I am still getting an error message as follows: SignatureDoesNotMatch
The request signature we calculated does not match the signature you provided. Check your key and signing method.
Interestingly the canonical request seems to read as a GET request still: GET /005.jpg X-Amz-Algorithm=AWS4-HMAC-SHA256&X-Amz-Credential=AKIAJB7L5VWCPYFXDA4A%2F20180830%2Feu-west-2%2Fs3%2Faws4_request&X-Amz-Date=20180830T090828Z&X-Amz-Expires=500&X-Amz-SignedHeaders=content-type%3Bhost content-type: host:imgictesting-localtesting.s3.amazonaws.com content-type;host UNSIGNED-PAYLOADMy code is as follows: class GetS3SignedUrl(View):
JS/AJAX Function:
Any ideas would be much appreciated, Thanks! |
This one took me a while to figure out. It turned out that the issue was I was passing in the wrong region to the S3 client call on the server. I didn't dig into why this fixed my issue - my bucket is encrypted, but a test bucket I first used was unencrypted and worked with the original region. I tried all the different tricks (creating a dummy file, trying different content types, etc). Fixing the region is what did it for me though. Before that I got a vague message "The bucket you are attempting to access must be addressed using the specified endpoint" In hopes of helping others, this is what I had: Server (note I AWS credentials in environmental variables):
Bucket CORS config:
client
|
After looking through all the related comments, how I got this working was by setting the correct s3_client = session.client(
's3', region_name='eu-central-1', endpoint_url='https://s3.eu-central-1.amazonaws.com'
) |
I had a similar problem. I was getting 403 error with no message. It was missing HTTP header in Params getSignedUrl = (file, callback) => {
axios.get("/api/s3/sign-upload", {
params: {
objectName: file.name,
contentType: file.type,
dir: this.props.dir,
}
})
.then(res => {
callback(res.data);
})
.catch(error => {
console.error(error);
});
}
<ReactS3Uploader
className={"todo"}
getSignedUrl={this.getSignedUrl}
accept="image/*"
onProgress={this.onProgress}
onError={this.onError}
onFinish={this.onFinish}
uploadRequestHeaders={{
"x-amz-acl": "public-read"
}}
contentDisposition="auto"
/> server import boto3
from flask import current_app, Blueprint, request, jsonify
import os
FIVE_MINUES = 5 * 60
s3_blueprint = Blueprint("s3", __name__)
@s3_blueprint.route("/sign-upload")
def sign_s3_upload():
s3 = boto3.client(
"s3",
aws_access_key_id=current_app.config["AWS_ACCESS_KEY_ID"],
aws_secret_access_key=current_app.config["AWS_SECRET_ACCESS_KEY"],
)
object_name = request.args.get("objectName")
dir = request.args.get("dir")
content_type = request.args.get("contentType")
url = s3.generate_presigned_url(
ClientMethod="put_object",
Params={
"Bucket": current_app.config["AWS_USER_UPLOAD_BUCKET"],
"Key": f"{dir}/{object_name/",
"ContentType": content_type,
"ACL": "public-read",
},
ExpiresIn=FIVE_MINUES,
)
return jsonify({"signedUrl": url}) |
For those who stumble across this post, I solved something similar (after much struggle) over here. |
I am managed to generate pre-signed url but i am getting this error message SignatureDiesNotmatch import boto3 s3_con = boto3.client('s3', 'us-west-2',) |
anybody help please |
@dino-cell #1149 (comment) Try adding endpoint_url to client: s3_con = boto3.client(
's3', region_name='us-west-2', endpoint_url='https://s3.us-west-2.amazonaws.com',
) |
hi eguven, thank you for responding but still i get |
@eguven SignatureDoesNotMatch |
(after 3+ hours of debugging and almost smashing the keyboard....) in the response, it's telling you which header is missing: <Error>
<Code>SignatureDoesNotMatch</Code>
<Message>The request signature we calculated does not match the signature you provided. Check your key and signing method.</Message>
<!-- .... -->
<CanonicalRequest>PUT (your pre-signed url)
content-type:image/jpeg
host:s3.eu-west-2.amazonaws.com
x-amz-acl:
content-type;host;x-amz-acl
UNSIGNED-PAYLOAD</CanonicalRequest> Needed a def python_presign_url():
return s3.generate_presigned_url('put_object', Params={
'Bucket': bucket_name,
'Key': filename,
'ContentType': type,
'ACL':'public-read' # your x-amz-acl
})
|
Dude you made my day. thank you. |
s3_con = boto3.client(
's3', region_name='us-west-2', endpoint_url='https://s3.us-west-2.amazonaws.com',
) this works, but why? I fixed this same issue before by specifying signature v4 and one time it was fixed by specifying region_name only, it makes no sense. |
Above code is my python code which generating signed url but when I am trying to upload image using signed url I am getting error message from AWS
SignatureDoesNotMatch
If I will remove ContentType from above code, I am able to upload image but content type is set as
application/x-www-form-urlencoded; charset=UTF-8
.I have to set content type as image/jpg or image/png because while accessing, I have to send it to the third party application who needs content type to be set properly.
I am very new to AWS section integration.
The text was updated successfully, but these errors were encountered: