1

I would like to generate a signed URL in Django, send it to frontend using Axios Ajax and then using that url upload a file directly from Vue JS to S3. In following code when user clicks on upload button - Vue method uploadButtonClicked is called which calls Django function ajaxSendPresignedUrlForS3 which generates presigned post url. This url is passed back to vue uploadButtonClicked and then vue method uploadFile is called.

So far url generation is sucessful. But on posting file to S3 bucket I get error Error: Request failed with status code 403. I have been reading around and making some modifications with the code which results in new errors like 412, 405 etc.

Django code

def ajaxSendPresignedUrlForS3(request):
    input=json.loads(request.body.decode('utf-8')); print('ajaxSendPresignedUrlForS3');
    S3_BUCKET = os.environ.get('S3_BUCKET')
    file_name = input['file_name'][0]
    file_type = input['file_type'][0]
    s3 = boto3.client('s3',
            aws_access_key_id=os.environ.get('AWS_ACCESS_KEY_ID'),
            aws_secret_access_key=os.environ.get('AWS_SECRET_ACCESS_KEY'),
            region_name='us-east-2',
            config=Config(signature_version='s3v4'),
            )
    presigned_post = s3.generate_presigned_post(
            Bucket = S3_BUCKET,
            Key = file_name,
            Fields = {"Content-Type": 'multipart/form-data'},
            Conditions = [{"Content-Type": 'multipart/form-data'}],

            ExpiresIn = 300 #seconds
    )
    return JsonResponse({'data': presigned_post})

Javascript Vue code

Vue method 1 :

uploadButtonClicked:function(){ //gettting presigned POST URL from django
          console.log('uploadButtonClicked');
          //call axios ajax to get presigned URL from django and then upload file to s3 using axios func "upLoadFile"
          axios({
                method: 'post',
                baseURL: window.location.origin, //we need base url coz this ajax can be called from any page on timeout
                url: 'main/ajaxSendPresignedUrlForS3/',
                data: {
                      file_name: this.inputFilesName,
                      file_type: this.inputFilesType,
                    },
                responseType: 'json', //server will response with this datatype
              })
              .then ( function (response){
                  data=response.data;
                  console.log('uploadButtonClicked succes. data =',data ); //works
                  this.upLoadFile(data); //upload file to S3

              }.bind(this))
              .catch ( function (error){
                console.log('uploadButtonClicked error=',error);
              });

      },

Vue method 2:

upLoadFile:function(data){ //upload file directly to s3
          console.log('upLoadFile')
          var postData = new FormData(); //its type to JS inbuilt form
          console.log('data.data.fields=',data.data.fields,'\nKeys =')
          for(key in data.data.fields){
              console.log(key);
              postData.append(key, data.data.fields[key]);
          }
          postData.append('file', document.getElementById('id_inputButtonReal').files[0]);
          console.log('postData=',postData)
          axios({
                method: 'get',
                url: data.data.url+new Date().getTime(),
                data: {
                      postData: postData,
                    },
                // responseType: 'json', //server will response with this datatype
              })
              .then ( function (response){
                  data=response.data;
                  console.log('upLoadFile success');

              }.bind(this))
              .catch ( function (error){
                console.log('upLoadFile error=',error);
              });
      },

I was able to upload file to s3 directly from Django though. Which probably means my python part is correct:

    from boto3.s3.transfer import S3Transfer
    myfile='/home/user/img1.jpg';
    transfer = S3Transfer(s3); #s3 is declared in above code
    transfer.upload_file(myfile, S3_BUCKET,'snake2.jpg') ; print('upload successful');

Thanks

2
  • Have you tried anything? Commented Apr 27, 2019 at 8:23
  • Yes I updated my question with code I tried so far Commented May 1, 2019 at 18:47

1 Answer 1

3

Error explaination: I was sending data to S3 in form of dictionary of dictionary. S3 likes direct postData.

Above Django code is perfectly fine. I have consolidated Javascript code into a smaller version below for comprehension.

  uploadButtonClickedforStackoverflow:function(){ //gettting presigned POST URL from django and then uploading file to S3 using that url
      axios({
            method: 'post',
            baseURL: window.location.origin, //we need base url coz this ajax can be called from any page on timeout
            url: 'main/ajaxSendPresignedUrlForS3/',
            data: { file_name: 'snake.jpg' },//sending filename to django so it can build a presigned POST URL granting frontend permission to upload
            responseType: 'json', //server will response with this datatype
          })
          .then ( function (response){
              presigned_post=response.data
              var postData = new FormData(); //its type to JS inbuilt form
              for(key in presigned_post.fields){ //appending all keys sent by django to our JS FormData object
                    postData.append(key, presigned_post.fields[key]);
              }
              var file=document.getElementById('id_inputButtonReal').files[0]; //id_inputButtonReal is id of HTML input element
              postData.append('file', file);
              axios({
                    method: 'post',
                    url: presigned_post.url,
                    data: postData, //dont sent data as {data:postData}, instead S3 likes it just by itself
                    })
          });

  },
Sign up to request clarification or add additional context in comments.

1 Comment

Great post! Due to ambiguity of the "403 response" from the presigned response by AWS, I landed on your answer and found my code is similar. My problem was resolved by granting the backend code "s3:PutObject" permission.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.