从vuejs和Django后端将文件上传到s3

时间:2019-04-27 03:46:03

标签: django vue.js amazon-s3

我想在Django中生成一个签名的URL,使用Axios Ajax将其发送到前端,然后使用该URL将文件直接从Vue JS上传到S3。在下面的代码中,当用户单击上传按钮时-调用Vue方法uploadButtonClicked,该方法调用Django函数ajaxSendPresignedUrlForS3,该函数生成带预签名的帖子网址。该URL传递回vue uploadButtonClicked,然后调用vue方法uploadFile

到目前为止,URL生成成功。但是在将文件发布到S3存储桶时,出现错误Error: Request failed with status code 403。我一直在阅读并对代码进行一些修改,导致出现诸如412、405等新错误。

Django代码

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代码

Vue方法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方法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);
              });
      },

尽管如此,我还是可以直接从Django将文件上传到s3。这可能意味着我的python部分是正确的:

    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');

谢谢

1 个答案:

答案 0 :(得分:0)

错误说明:我正在以字典字典的形式向S3发送数据。 S3喜欢直接发布数据。

以上Django代码都很好。我已经将Javascript代码合并为一个较小的版本,以供理解。

  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
                    })
          });

  },