问题
Uploading a file to Amazon S3 using presigned URL which has Signature, Expiration and AccessKey, using the following code I'm able to upload the file using normal java code but same code in Android gives me 403 error. Presigned URL is generate using Amazon SDK
I have read http://developer.android.com/reference/java/net/HttpURLConnection.html and http://android-developers.blogspot.com/2011/09/androids-http-clients.html but not able to figure out what header param should i use, I guess in android it is setting headers in request which server rejects
HttpURLConnection connection=(HttpURLConnection) url.openConnection();
connection.setDoOutput(true);
connection.setRequestMethod("PUT");
OutputStreamWriter out = new OutputStreamWriter(connection.getOutputStream());
out.write("This text uploaded as object.");
out.close();
int responseCode = connection.getResponseCode();
Exception: 403; Signature don't match :-o
Has anyone come across this issue? Or more details into which Header-parameters are added behind the scenes from android library?
回答1:
please set your content type like this:
connection.setRequestProperty("Content-Type"," ");
because the default for HttpsUrlConnection Automatically generates content type as:
"Content-Type: application/x-www-form-urlencoded"
This will cause signature mismatch.
回答2:
So after some trial/error/search found out the issue:
While creating a presigned URL is it important to specify Content-Type (based on your data) or else you will keep getting 403 Signature don't match, the contentType that you specify here should be mentioned in HttpURLConnection connection object
string s3url = s3Client.GetPreSignedURL(new GetPreSignedUrlRequest()
.WithBucketName(bucketName)
.WithKey(keyName)
.WithContentType("application/octet-stream") // IMPORTANT
.WithVerb(HttpVerb.PUT)
.WithExpires(<YOUR EXPIRATION TIME>);
Inside your connection..add this to the code in question after ("PUT")
connection.setFixedLengthStreamingMode(< YOUR DATA.LENGTH ?>);
connection.setRequestProperty("Content-Type","application/octet-stream");
Check this bug, latest SDK should let you set Content-Type
回答3:
The way to generate the URL:
private static URL generateRUL(String objectKey, String ACCESS_KEY, String SECRET_KEY, String BUCKET_NAME) {
AmazonS3 s3Client = new AmazonS3Client(new BasicAWSCredentials(ACCESS_KEY, SECRET_KEY));
URL url = null;
try {
GeneratePresignedUrlRequest request = new GeneratePresignedUrlRequest(BUCKET_NAME, objectKey);
request.setMethod(com.amazonaws.HttpMethod.PUT);
request.setExpiration(new Date( System.currentTimeMillis() + (60 * 60 * 1000)));
// Very important ! It won't work without adding this!
// And request.addRequestParameter("Content-Type", "application/octet-stream") won't work neither
request.setContentType("application/octet-stream");
url = s3Client.generatePresignedUrl(request );
} catch (AmazonServiceException exception) {
} catch (AmazonClientException ace) { }
return url;
}
The way to upload the file:
public int upload(byte[] fileBytes, URL url) {
HttpURLConnection connection = (HttpURLConnection) url.openConnection();
connection.setDoOutput(true);
connection.setRequestMethod("PUT");
connection.setRequestProperty("Content-Type", "application/octet-stream"); // Very important ! It won't work without adding this!
OutputStream output = connection.getOutputStream();
InputStream input = new ByteArrayInputStream(fileBytes);
byte[] buffer = new byte[4096];
int length;
while ((length = input.read(buffer)) > 0) {
output.write(buffer, 0, length);
}
output.flush();
return connection.getResponseCode();
}
回答4:
Are you using the AWS SDK for Android? That has the correct APIs and I suspect it will be easier to upload to S3 using that, and more secure for that matter. I believe they have tutorials, code samples, and demos there as well.
There is also an API for S3 and other classes you may need. A PutObjectRequest is what will help you upload the file from the phone client and can be useful in your case.
回答5:
I had this same problem. Here's the reason why I faced it:
A pre-signed URL gives you access to the object identified in the URL, provided that the creator of the pre-signed URL has permissions to access that object. That is, if you receive a pre-signed URL to upload an object, you can upload the object only if the creator of the pre-signed URL has the necessary permissions to upload that object.
http://docs.aws.amazon.com/AmazonS3/latest/dev/PresignedUrlUploadObject.html
Once I gave my lambda function PutObject permissions on that s3 bucket then it worked!
来源:https://stackoverflow.com/questions/15643135/android-httpurlconnection-put-to-amazon-aws-s3-403-error