Skip to content

How to upload to S3 after getting the signed Url from the backend? #327

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

Open
harishankards opened this issue May 13, 2018 · 23 comments
Open

Comments

@harishankards
Copy link

Hi,
In my project, I'm using nodejs for backend API and vue for the frontend. I'm using this vue-dropzone in my project.

So, when a file is successfully uploaded into the frontend, I'm taking the filename and type to the backend to create a signedUrl to upload the files in AWS S3. Please refer to the code below.

dropzoneOptions: {
          url: 'http://localhost:3000/attachments', //I'm sending the req to my backend to get the signedUrl
          thumbnailWidth: 150,
          maxFilesize: 0.5,
          headers: { 'Authorization': 'Bearer ' + this.token },
          addRemoveLinks: true,
          dictDefaultMessage: "<i class='fa fa-cloud-upload'></i>UPLOAD ME"
        }

and the success function

vsuccess (file, response) {
        console.log('it is success', file, response) // this response has the signedUrl from my backend       
      }

Now, that I have the signedUrl here, how do I send the file to the S3?

In the example provided by you here, the signedURL seemed to be put in a hardcoded way.
But, I'm doing in a dynamic way. So, how do I achieve that?

Thanks in advance.

@vrajroham
Copy link
Collaborator

Hi @harishankards ,

The signedURL seemed to be put in a hardcoded way

The signed URL you found documentation page is sample response and that too is generated dynamically using this php script.


I'm doing in a dynamic way. So, how do I achieve that?

As per my understating, You are getting signed url from server endpoint http://localhost:3000/attachments and you want to upload your file to AWS S3 using that pre-signed url. right?

You need to add additional prop awss3 to dropzone as below,

<vue-dropzone 
      :awss3="awss3"
      v-on:vdropzone-s3-upload-error="s3UploadError"
      v-on:vdropzone-s3-upload-success="s3UploadSuccess">
</vue-dropzone>

Add awss3 prop with dropzoneOptions as below,

dropzoneOptions: {
      url: 'http://localhost:3000/file-upload', // File upload endpoint, not signed url endpoint
      thumbnailWidth: 150,
      maxFilesize: 0.5,
      headers: { 'Authorization': 'Bearer ' + this.token },
      addRemoveLinks: true,
      dictDefaultMessage: "<i class='fa fa-cloud-upload'></i>UPLOAD ME"
},
awss3: {
      signingURL: 'http://localhost:3000/attachments', //Where you will get signed url
      headers: {},
      params : {},
      sendFileToServer : true //If you want to upload file to your server along with s3
}

Hope this helps you.

@harishankards
Copy link
Author

Hi @vrajroham,

Thanks for the response.

I tried the same way as you mentioned:

dropzoneOptions: {
          url: 'http://localhost:3000/attachments',
          thumbnailWidth: 150,
          maxFilesize: 0.5,
          headers: { 'Authorization': 'Bearer ' + this.token },
          addRemoveLinks: true,
          dictDefaultMessage: "<i class='fa fa-cloud-upload'></i>UPLOAD ME"
        },
        awss3: {
          signingURL: 'http://localhost:3000/attachments', // Where you will get signed url
          headers: { 'Authorization': 'Bearer ' + this.token },
          params: {},
          sendFileToServer: false // If you want to upload file to your server along with s3
        }

and the my backend server sent this as response:

{ method: 'PUT',
  key: 'images/my_image.jpg',
  url: 'https://student-burger.s3.amazonaws.com/images/my_image.jpg?X-Amz-Algorithm=AWS4-HMAC-SHA256&X-Amz-Credential=AKIAITZKxxxxHPB7M4UA%2F20180514%2Fus-east-1%2Fs3%2Faws4_request&X-Amz-Date=20180xxx14T100307Z&X-Amz-Expires=86400&X-Amz-Signature=e07717679ee7xxxxxxxx6b366a9812a53e635f5&X-Amz-SignedHeaders=host' }

But, nothing happened in the frontend. So, I checked the console, it is displaying this error

Uncaught TypeError: Cannot convert undefined or null to object

at vue2Dropzone.js?2af3:1.

Also, I want to send the filename to my backend for signing URL. How do I get those?

@vrajroham
Copy link
Collaborator

@harishankards I got it. I think you should read the documentation completly.

Uncaught TypeError: Cannot convert undefined or null to object

Your response is in invalid format. Your endpoint should return signature object exactly as mentioned in docs. Currently, it's returning all values appended to URL.

Your response should be like as below, More info here

{
   "signature":{
      "Content-Type":"",
      "acl":"public-read-write",
      "success_action_status":"201",
      "policy":"abc123",
      "X-amz-credential":"AKIAIM3WELV3PLALOYDQ\/20171012\/us-west-2\/s3\/aws4_request",
      "X-amz-algorithm":"AWS4-HMAC-SHA256",
      "X-amz-date":"20171012T054729Z",
      "X-amz-signature":"5227d84360d92ef8al45549805b3746f2f1d6641df8986aamcr939c35513cd7c",
      "key":"images/my_image.jpg"
   },
   "postEndpoint":"\/\/student-burger.s3.amazonaws.com\/images"
}

Also, I want to send the filename to my backend for signing URL. How do I get those?

Already note about this is provided in docs.

Firstly, your file is uploaded to S3 then request is sent to your server with file as expected behaviour. But extra field to your request will be added as s3ObjectLocation containing location of your S3 object/file, which you may require to store in database.

@harishankards
Copy link
Author

Hi @vrajroham,

Got it. Thanks for the explanation. Now I'm able to solve the previous issue, and got up with another one now.

{
"signature": {
"Content-Type":"",
"acl":"public-read-write",
"success_action_status":"201",
"policy":"Policy1xxxxx45161391",
"X-Amz-Algorithm":"AWS4-HMAC-SHA256",
"X-Amz-Credential":"AKxxTZKxxxxxxB7M4UA%2F20180514%2Fus-east-1%2Fs3%2Faws4_request",
"X-Amz-Date":"20180514T130047Z",
"X-Amz-Expires":"86400",
"X-Amz-Signature":"25cadc433cxxxxxxxxbdde29e0ea8ec150134bba753b1e568822696094a8c82c58",
"X-Amz-SignedHeaders":"host",
"key":""
},
"postEndPoint":"//student-burger.s3.amazonaws.com/images"
}

The above is the one I'm sending from backend. Now, the error I'm getting is:
POST http://localhost:8080/student/projects/undefined 404 (Not Found) at dropzone.js?a0b9:2777.

Not sure, why it is coming. But this error where I'm receiving in the page has the route http://localhost:8080/student/projects/new.

@vrajroham
Copy link
Collaborator

  • What is your url in dropzoneOptions?
  • Are your files getting uploaded to S3? (Check network tab)

@harishankards
Copy link
Author

@vrajroham ,

This is my dropzoneOptions :

dropzoneOptions: {
          url: 'http://localhost:3000/attachments',
          thumbnailWidth: 150,
          maxFilesize: 0.5,
          headers: { 'Authorization': 'Bearer ' + this.token },
          addRemoveLinks: true,
          dictDefaultMessage: "<i class='fa fa-cloud-upload'></i>UPLOAD ME"
        }

Nope. My files are not getting uploaded to S3. No sign of that post/put request to S3 in networks tab.

@vrajroham
Copy link
Collaborator

Share your complete component and js code.

@abdulbasith7145
Copy link

@vrajroham
I'm encountering an issue similar to this, can you please check this issue #328

@vrajroham
Copy link
Collaborator

@abdulbasith7145 Will check it.

@harishankards
Copy link
Author

harishankards commented May 15, 2018

Sure @vrajroham, Please have a look below.

<template>
  <vue-dropzone ref="myVueDropzone" :destroyDropzone="false" 
    @vdropzone-success="vsuccess" @vdropzone-removed-file="vremoved"
    id="dropzone" :options="dropzoneOptions"
    :awss3="awss3"
    v-on:vdropzone-s3-upload-error="s3UploadError"
    v-on:vdropzone-s3-upload-success="s3UploadSuccess">
  </vue-dropzone>
</template>

<script>
  import vue2Dropzone from 'vue2-dropzone'
  import 'vue2-dropzone/dist/vue2Dropzone.css'
  import { eventBus } from '../../main.js'

  export default {
    name: 'upload',
    components: {
      vueDropzone: vue2Dropzone
    },
    data: function () {
      return {
        token: this.$ls.get('token'),
        dropzoneOptions: {
          url: 'http://localhost:3000/attachments',
          thumbnailWidth: 150,
          maxFilesize: 0.5,
          headers: { 'Authorization': 'Bearer ' + this.token },
          addRemoveLinks: true,
          dictDefaultMessage: "<i class='fa fa-cloud-upload'></i>UPLOAD ME"
        },
        awss3: {
          signingURL: 'http://localhost:3000/attachments/signedUrlPut', // Where you will get signed url
          headers: { 'Authorization': 'Bearer ' + this.token },
          params: {},
          sendFileToServer: false 
        }
      }
    },
    methods: {
      vsuccess (file, response) {
        this.$store.state.projectUploadedFile.push({
          filename: file.name,
          signedUrl: response.url
        })
        eventBus.$emit('uploadedFile', {
          projectUploadedFiles: this.$store.state.projectUploadedFile
        })
      },
      vremoved (file, error, xhr) {
        const filename = file.name
        const signedUrl = this.$store.state.projectUploadedFile.map(item => {
          if (item.filename === filename) {
            const index = this.$store.state.projectUploadedFile.indexOf(item)
            this.$store.state.projectUploadedFile.splice(index, 1)
            return item.signedUrl
          }
        })
        this.$http({
          method: 'delete',
          url: '/attachments',
          data: {
            filepath: filepath
          },
          headers: {
            'Authorization': 'Bearer ' + this.token
          }
        })
        .then(function (attachmentDeleted) {
          console.log('attachment deleted', attachmentDeleted)
        })
        .catch(function (attachmentDeleteErr) {
          console.log('unable to delete attachment', attachmentDeleteErr)
        })
      },
      s3UploadError () {
        console.log('s3UploadError')
      },
      s3UploadSuccess () {
        console.log('s3UploadSuccess')
      }
    },
    created () {
      this.$store.state.projectUploadedFile = []
    }
  }
</script>

So, this is my component and I'm importing this component to the other components wherever needed.

@vrajroham
Copy link
Collaborator

vrajroham commented May 15, 2018

@harishankards Just to test, please do following steps and provide the outcome.

Let me know the outcome.

@harishankards
Copy link
Author

Hi @vrajroham,
I got two errors in the console itself.

  1. POST https://rowanwins.github.io/vue-dropzone/docs/dist/undefined 502 () at urlsigner.js:82
  2. Network Error : Could not send request to AWS. (Maybe CORS error) at bundle.3fb36dea395ce2168db7.js:1805
    I doubt why CORS is happening as it has the signed URL with it.

@harishankards
Copy link
Author

Hi @vrajroham,

Just wanted to check whether do I have got any updates.

Looking forward

@WajdiF
Copy link

WajdiF commented May 19, 2018

Hi @vrajroham
I have the exact same problem, could you please help ?
Best regards

@harishankards
Copy link
Author

Welcome to the club @WajdiF. If you have got some solution, please let me know.

Seems @vrajroham is busy

@rowanwins
Copy link
Owner

Hi @harishankards & @WajdiF

Can either of you provide a more detailed error message?

For your s3UploadError method you should be able to pass in the actual error message

      s3UploadError (e) {
        console.log(e)
      },

And also just a friendly reminder this is an open-source project - we do our best to support users but we do ask for a bit of patience as both @vrajroham & myself have families to care for, full-time jobs, and other various life responsibilities. We maintain this component in our spare time with no financial gain. Anyone is free to dig into the source code to work out what is going on.

Thanks,
Rowan

@vrajroham
Copy link
Collaborator

vrajroham commented May 21, 2018

@harishankards @WajdiF @abdulbasith7145

I spent much time to replicate your issue, but I'cant! Maybe I need more information, which can be provided as Rowan suggested in the previous comment. I have recorded sample video where, you can see the flow how I'm uploading an image to s3, from this doc page (testing purpose).

Note: In the video, I used ngrok tool to expose my (url signer endpoint) local endpoint publicly due to pre-flight error in js.

It's really very hard to take out time from daily schedule, though we are trying our best. What I suggest to you guys is, any of you can look into the code and find out where it is going wrong. It is really simple to start with, like below.

  • Clone the repo
  • npm install / yarn install
  • npm run start / yarn start

If anyone can provide more specific error then it will be really helpful.

Thanks.

@harishankards
Copy link
Author

Hey @rowanwins,

Thanks for the detailed explanation and yes I understand bro. Regarding the solution, for me, it doesn't go until s3UploadError(e){} and all.

Whenever I upload something, in the console it throws: http://localhost:8080/student/projects/undefined 404 (Not Found) at dropzone.js?a0b9:2777 but I don't know how it is taking that URL.

And @vrajroham, I clearly understand and thanks a lot for the video. I also used ngrok and I tried with this page, but this time it throws this error:
POST https://rowanwins.github.io/vue-dropzone/docs/dist/undefined 502() at urlsigner.js:82.

So, my find is the request is made to whatever the page's current url + undefined. Because when I tried with mine it was http://localhost:8080/student/projects/undefined and with your docs page it is POST https://rowanwins.github.io/vue-dropzone/docs/dist/undefined.

So, something fishy is happening there only.

Note: I also understand that contribution is needed to the repo. But I'm currently locked down until this month end. I shall contribute once my sprint is over.

@WajdiF
Copy link

WajdiF commented May 23, 2018

Hi, @rowanwins,@vrajroham

Thanks a lot for your help and for this library, i totally understand your lack of time. I will do my best to help solve this issue (if any :) ).

I cloned the lib and tested locally, i found a weird behavior :
urlSigner.js line 56, when we try to get the response.postEndPoint the result is undefined. this is the root cause of the issue.
The wierd thing is postEndPoint attribute is defined but accessing it with '.' returns an undefined but accessing with braces returns the correct values, hereunder some logs (after cleaning signature and private data) :

{postEndPoint: "//s3-eu-west-3.amazonaws.com/myBucket", signature: {…}}
response.hasOwnProperty('postEndpoint') => false
[] access, response['postEndPoint'] => //s3-eu-west-3.amazonaws.com/myBucket

the following exception is thrown when the postEndPoint is undefined :
urlsigner.js?77a0:87 POST http://localhost:8081/undefined 404 (Not Found)

do you

@rowanwins
Copy link
Owner

Gday @WajdiF

Great digging! I wonder if this is a browser compatibility issue, are you using IE by any chance, if so perhaps #325 is related?

@horaciosolorio
Copy link

@harishankards

Hi can you help me please i using node but y only have URL from S3 AWS SDK in back end but i dont understand how to get in node return "signature" object.

Below is the current code in node i have

const parametros = {
Bucket: myBucket,
Key: myKey,
Expires : signedUrlExpireSeconds,
ACL:'public-read',
ContentType:'image/png'
}

var url = s3.getSignedUrl('putObject', parametros , function (err, url) {
if (err) {
res.send(err)
} else {
console.log(url)
res.send(url)
}
})

Please help me

@ShrJoshi28
Copy link

how to solve this issue-
ccess to XMLHttpRequest at 'https://4kd2hi3ere.execute-api.us-east-1.amazonaws.com/default/myfirstvuecode' from origin 'http://localhost:8080' has been blocked by CORS policy: Response to preflight request doesn't pass access control check: No 'Access-Control-Allow-Origin' header is present on the requested resource.

@vrajroham vrajroham self-assigned this Feb 19, 2019
@ianwow
Copy link

ianwow commented Oct 3, 2019

@ShrJoshi28 In the past I've gotten a CORS error because I didn't add a CORS policy to my S3 bucket. I've also gotten that error because I was not using a region-specific URL for my upload to S3. In that case my browser said I had a CORS error even though it was not actually a CORS error but rather a redirect error (something about preflight request couldn't be redirected). If you look closely at the redirect you'll see the browser was redirected to a region-specific URL.

If you're not using the us-east-1 (Virginia) region, you need to upload to a region-specific URL. For more info, see "region-specific endpoints" at https://docs.aws.amazon.com/AmazonS3/latest/dev/UsingBucket.html

I created a pull request to ensure Dropzone does this correctly:
#494

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

8 participants