adopted-ember-addons / ember-file-upload

File uploads for Ember apps
https://ember-file-upload.pages.dev/
Other
201 stars 119 forks source link

S3 Upload #52

Open dima-shulga opened 7 years ago

dima-shulga commented 7 years ago

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.

RuslanZavacky commented 7 years ago

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?

tim-evans commented 7 years ago

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

schleifer-john commented 6 years ago

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();
      });
    }
  }
});
Parrryy commented 6 years ago

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

jelhan commented 4 years ago

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.

mcfiredrill commented 4 years ago

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 ?

jelhan commented 4 years ago

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

allthesignals commented 4 years ago

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.