ember-file-upload icon indicating copy to clipboard operation
ember-file-upload copied to clipboard

S3 Upload

Open dima-shulga opened this issue 7 years ago • 8 comments

I've done everything like in your tutorial except your Ruby script. I've used node js aws-sdk:

const aws = require('aws-sdk');
var config = require('../../server/config.json');
aws.config = config.aws;

    const s3 = new aws.S3();
    const s3Params = {
      Bucket: config.aws.bucket,
      Key: instance.filename,
      Expires: 60,
      ACL: 'public-read'
    };

    s3.getSignedUrl('putObject', s3Params, (err, signedUrl) => {
      if(err){
        return next(err);
      }
      instance.uploadUrl = signedUrl;
      next();
    });

So I've got signed URL

Request URL:https://f.workdoer.com.s3.amazonaws.com/IMG_4875.PNG.png?AWSAccessKeyId=AKIAIFVWPIVVIGIONPXA&Expires=1502381111&Signature=Af9meXlMA7qj5HYALs7LzhWaftc%3D&x-amz-acl=public-read

Request headers:
PUT /IMG_4875.PNG.png?AWSAccessKeyId=AKIAIFVWPIVVIGIONPXA&Expires=1502381111&Signature=Af9meXlMA7qj5HYALs7LzhWaftc%3D&x-amz-acl=public-read HTTP/1.1
Host: f.workdoer.com.s3.amazonaws.com
Connection: keep-alive
Content-Length: 130838
Accept: application/json,text/javascript
Origin: http://localhost:4200
User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_12_6) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/60.0.3112.90 Safari/537.36
Content-Type: multipart/form-data; boundary=----WebKitFormBoundarygDui83iohothuh7E
Referer: http://localhost:4200/app/team/1/project/15/list/task/2074
Accept-Encoding: gzip, deflate, br
Accept-Language: ru,en-US;q=0.8,en;q=0.6,uk;q=0.4,it;q=0.2,de;q=0.2
Request Payload:
------WebKitFormBoundarygDui83iohothuh7E
Content-Disposition: form-data; name="Content-Type"

image/png
------WebKitFormBoundarygDui83iohothuh7E
Content-Disposition: form-data; name="file"; filename="IMG_4875.PNG.png"
Content-Type: image/png


------WebKitFormBoundarygDui83iohothuh7E--

And I've got from Amazon:

<Error><Code>SignatureDoesNotMatch</Code><Message>The request signature we calculated does not match the signature you provided. Check your key and signing method.</Message><AWSAccessKeyId>AKIAIFVWPIVVIGIONPXA</AWSAccessKeyId><StringToSign>PUT

multipart/form-data; boundary=----WebKitFormBoundarygDui83iohothuh7E
1502381111
x-amz-acl:public-read
/f.workdoer.com/IMG_4875.PNG.png</StringToSign><SignatureProvided>Af9meXlMA7qj5HYALs7LzhWaftc=</SignatureProvided>

Something wrong with your request, because when I put it with CURL:

curl -k -X PUT -T "IMG_4875.PNG.png" "https://f.workdoer.com.s3.amazonaws.com/IMG_4875.PNG.png?AWSAccessKeyId=AKIAIFVWPIVVIGIONPXA&Expires=1502381111&Signature=Af9meXlMA7qj5HYALs7LzhWaftc%3D&x-amz-acl=public-read"

It's successfully uploaded. What I'm doing wrong?

Here is my bucket policy:

    {
        "Version": "2008-10-17",
        "Statement": [
            {
                "Sid": "Allow Public Access to All Objects",
                "Effect": "Allow",
                "Principal": {
                    "AWS": "*"
                },
                "Action": [
                    "s3:DeleteObject",
                    "s3:GetObject",
                    "s3:PutObject"
                ],
                "Resource": "arn:aws:s3:::f.workdoer.com/*"
            }
        ]
    }

And cors:


    <?xml version="1.0" encoding="UTF-8"?>
    <CORSConfiguration xmlns="http://s3.amazonaws.com/doc/2006-03-01/">
    <CORSRule>
        <AllowedOrigin>*</AllowedOrigin>
        <AllowedMethod>PUT</AllowedMethod>
        <AllowedMethod>POST</AllowedMethod>
        <MaxAgeSeconds>3000</MaxAgeSeconds>
        <ExposeHeader>Location</ExposeHeader>
        <AllowedHeader>Content-Type</AllowedHeader>
        <AllowedHeader>x-amz-acl</AllowedHeader>
        <AllowedHeader>origin</AllowedHeader>
        <AllowedHeader>accept</AllowedHeader>
    </CORSRule>
    </CORSConfiguration>

Thanks for any help.

dima-shulga avatar Aug 10 '17 16:08 dima-shulga

I actually have the same issue, but I am doing request from PHP. Can someone please verify, that Ruby version in Readme and the latest version of this bundle, actually can upload to S3?

RuslanZavacky avatar Aug 30 '17 22:08 RuslanZavacky

Let me look into this and reproduce this so I can debug.

tim-evans avatar Aug 31 '17 03:08 tim-evans

I ran into this same issue. The problem is the S3 example in the README says to use the file.upload method, but that method adds the multipart/form-data content type header which as mentioned here is not compatible with binary uploads to S3: https://github.com/aws/aws-sdk-js/issues/547#issuecomment-86873980

I saw this solution (https://github.com/tim-evans/ember-file-upload/pull/12#issuecomment-315375492) which mentions there is an alternative method called uploadBinary. As mentioned in the thread, the example in the README needs to be updated to the following for S3 uploads to work:

import Ember from 'ember';

const RSVP = Ember.RSVP;
const set = Ember.set;

export default Ember.Route.extend({
  actions: {
    uploadImage: function (file) {
      let model = this.modelFor(this.routeName);
      RSVP.cast(Ember.$.get('/api/s3_direct')).then(function (response) {
        return file.uploadBinary(response.url, {
          method: 'PUT',
          contentType: file.blob.type,
          data: JSON.stringify(response.credentials),
        });
      }).then(function (response) {
        set(model, 'url', response.headers.Location);
        return model.save();
      });
    }
  }
});

schleifer-john avatar Feb 23 '18 21:02 schleifer-john

Try adding Content-Type to S3Params when getting signed URL.

Parrryy avatar Mar 15 '18 11:03 Parrryy

Current documentation still uses file.upload() and not file.uploadBinary(): https://adopted-ember-addons.github.io/ember-file-upload/docs/aws

@schleifer-john do you have time to provide a pull request fixing that one?

Try adding Content-Type to S3Params when getting signed URL.

@Parrryy Is this something what should be added to our documentation? If so, please feel free to provide a pull request.

jelhan avatar Nov 08 '19 13:11 jelhan

I needed to use uploadBinary or else the formData seems to be concatenated to the beginning of the file. What is the use case for using upload instead of uploadBinary ?

mcfiredrill avatar Dec 22 '19 06:12 mcfiredrill

What is the use case for using upload instead of uploadBinary ?

uploadBinary enforces a Content-Type: 'application/octet-stream' which upload does not: https://github.com/adopted-ember-addons/ember-file-upload/blob/c2d9ae84f4846f2dc924f1436e9df66e0838fa04/addon/file.js#L277-L282

jelhan avatar Jan 06 '20 14:01 jelhan

When I specify a contentType for uploadBinary I get an error:

file.js:43 Uncaught (in promise) TypeError: Cannot create property 'Content-Type' on string '{"key":"asdf.jpg"}'

It seems to pass through uploadBinary, then call the regular upload method.

Did something about the API change? I am stringifying the data according to @schleifer-john 's approach.

allthesignals avatar Sep 23 '20 20:09 allthesignals