使用 Amazon S3 + Django 上传大文件 400 错误请求

问题描述 投票:0回答:1

我正在遵循这个课程,我认为我做的一切都是正确的,但它给了我

POST https://fakebucket.s3-eu-north-1.amazonaws.com/ 400(错误请求)

views.py

class FilePolicyAPI(APIView):
"""
This view is to get the AWS Upload Policy for our s3 bucket.
What we do here is first create a FileItem object instance in our
Django backend. This is to include the FileItem instance in the path
we will use within our bucket as you'll see below.
"""
permission_classes = [permissions.IsAuthenticated]
authentication_classes = [authentication.SessionAuthentication]

def post(self, request, *args, **kwargs):
    """
    The initial post request includes the filename
    and auth credientails. In our case, we'll use
    Session Authentication but any auth should work.
    """
    filename_req = request.data.get('filename')
    if not filename_req:
            return Response({"message": "A filename is required"}, status=status.HTTP_400_BAD_REQUEST)
    policy_expires = int(time.time()+5000)
    user = request.user
    username_str = str(request.user.username)
    """
    Below we create the Django object. We'll use this
    in our upload path to AWS. 

    Example:
    To-be-uploaded file's name: Some Random File.mp4
    Eventual Path on S3: <bucket>/username/2312/2312.mp4
    """
    file_obj = Post.objects.create(
        author = request.user, 
        title=filename_req
        )
    file_obj_id = file_obj.id
    upload_start_path = "{username}/".format(
                username = username_str,
                file_obj_id=file_obj_id
        )       
    _, file_extension = os.path.splitext(filename_req)
    filename_final = "{file_obj_id}{file_extension}".format(
                file_obj_id= file_obj_id,
                file_extension=file_extension

            )
    """
    Eventual file_upload_path includes the renamed file to the 
    Django-stored FileItem instance ID. Renaming the file is 
    done to prevent issues with user generated formatted names.
    """
    final_upload_path = "{upload_start_path}{filename_final}".format(
                             upload_start_path=upload_start_path,
                             filename_final=filename_final,
                        )
    if filename_req and file_extension:
        """
        Save the eventual path to the Django-stored FileItem instance
        """
        file_obj.existingPath = final_upload_path
        file_obj.save()

    policy_document_context = {
        "expire": policy_expires,
        "bucket_name": AWS_UPLOAD_BUCKET,
        "key_name": "",
        "acl_name": "private",
        "content_name": "",
        "content_length": 524288000,
        "upload_start_path": upload_start_path,

        }
    policy_document = """
    {"expiration": "2019-01-01T00:00:00Z",
      "conditions": [ 
        {"bucket": "%(bucket_name)s"}, 
        ["starts-with", "$key", "%(upload_start_path)s"],
        {"acl": "%(acl_name)s"},

        ["starts-with", "$Content-Type", "%(content_name)s"],
        ["starts-with", "$filename", ""],
        ["content-length-range", 0, %(content_length)d]
      ]
    }
    """ % policy_document_context
    aws_secret = str.encode(AWS_UPLOAD_SECRET_KEY)
    policy_document_str_encoded = str.encode(policy_document.replace(" ", ""))
    url = 'https://{bucket}.s3-{region}.amazonaws.com'.format(
                    bucket=AWS_UPLOAD_BUCKET,  
                    region=AWS_UPLOAD_REGION
                    )
    policy = base64.b64encode(policy_document_str_encoded)
    signature = base64.b64encode(hmac.new(aws_secret, policy, hashlib.sha1).digest())
    data = {
        "policy": policy,
        "signature": signature,
        "key": AWS_UPLOAD_ACCESS_KEY_ID,
        "file_bucket_path": upload_start_path,
        "file_id": file_obj_id,
        "filename": filename_final,
        "url": url,
        "username": username_str,
    }
    return Response(data, status=status.HTTP_200_OK)

和jquery:

    $(document).ready(function(){

    // setup session cookie data. This is Django-related
    function getCookie(name) {
        var cookieValue = null;
        if (document.cookie && document.cookie !== '') {
            var cookies = document.cookie.split(';');
            for (var i = 0; i < cookies.length; i++) {
                var cookie = jQuery.trim(cookies[i]);
                // Does this cookie string begin with the name we want?
                if (cookie.substring(0, name.length + 1) === (name + '=')) {
                    cookieValue = decodeURIComponent(cookie.substring(name.length + 1));
                    break;
                }
            }
        }
        return cookieValue;
    }
    var csrftoken = getCookie('csrftoken');
    function csrfSafeMethod(method) {
        // these HTTP methods do not require CSRF protection
        return (/^(GET|HEAD|OPTIONS|TRACE)$/.test(method));
    }
    $.ajaxSetup({
        beforeSend: function(xhr, settings) {
            if (!csrfSafeMethod(settings.type) && !this.crossDomain) {
                xhr.setRequestHeader("X-CSRFToken", csrftoken);
            }
        }
    });
    // end session cookie data setup. 



// declare an empty array for potential uploaded files
var fileItemList = []

// auto-upload on file input change.
$(document).on('change','.cfeproj-upload-file', function(event){
    var selectedFiles = $(this).prop('files');
    formItem = $(this).parent()
    $.each(selectedFiles, function(index, item){
        var myFile = verifyFileIsImageMovieAudio(item)
        if (myFile){
            uploadFile(myFile)
        } else {
            // alert("Some files are invalid uploads.")
        }
    })
    $(this).val('');

})



function verifyFileIsImageMovieAudio(file){
    // verifies the file extension is one we support.
    var extension = file.name.split('.').pop().toLowerCase(); //file.substr( (file.lastIndexOf('.') +1) );
    switch(extension) {
        case 'jpg':
        case 'png':
        case 'gif':
        case 'jpeg':
            return file  
        case 'mov':
        case 'mp4':
            return file
        default:
            notAllowedFiles.push(file)
            return null
    }
};

function constructFormPolicyData(policyData, fileItem) {
   var contentType = fileItem.type != '' ? fileItem.type : 'application/octet-stream'
    var url = policyData.url
    var filename = policyData.filename
    var repsonseUser = policyData.user
    // var keyPath = 'www/' + repsonseUser + '/' + filename
    var keyPath = policyData.file_bucket_path
    var fd = new FormData()
    fd.append('key', keyPath + filename);
    fd.append('acl','private');
    fd.append('Content-Type', contentType);
    fd.append("AWSAccessKeyId", policyData.key)
    fd.append('Policy', policyData.policy);
    fd.append('filename', filename);
    fd.append('Signature', policyData.signature);
    fd.append('file', fileItem);
    return fd
}

function fileUploadComplete(fileItem, policyData){
    data = {
        uploaded: true,
        fileSize: fileItem.size,
        file: policyData.file_id,

    }
    $.ajax({
        method:"POST",
        data: data,
        url: "/api/files/complete/",
        success: function(data){
            displayItems(fileItemList)
        },
        error: function(jqXHR, textStatus, errorThrown){ 
            alert("An error occured, please refresh the page.")
        }
    })
}

function displayItems(fileItemList){
    var itemList = $('.item-loading-queue')
    itemList.html("")
    $.each(fileItemList, function(index, obj){
        var item = obj.file
        var id_ = obj.id
        var order_ = obj.order
        var html_ = "<div class=\"progress\">" + 
          "<div class=\"progress-bar\" role=\"progressbar\" style='width:" + item.progress + "%' aria-valuenow='" + item.progress + "' aria-valuemin=\"0\" aria-valuemax=\"100\"></div></div>"
        itemList.append("<div>" + order_ + ") " + item.name + "<a href='#' class='srvup-item-upload float-right' data-id='" + id_ + ")'>X</a> <br/>" + html_ + "</div><hr/>")

    })
}


function uploadFile(fileItem){
        var policyData;
        var newLoadingItem;
        // get AWS upload policy for each file uploaded through the POST method
        // Remember we're creating an instance in the backend so using POST is
        // needed.
        $.ajax({
            method:"POST",
            data: {
                filename: fileItem.name
            },
            url: "/api/files/policy/",
            success: function(data){
                    policyData = data
            },
            error: function(data){
                alert("An error occured, please try again later")
            }
        }).done(function(){
            // construct the needed data using the policy for AWS
            var fd = constructFormPolicyData(policyData, fileItem)

            // use XML http Request to Send to AWS. 
            var xhr = new XMLHttpRequest()

            // construct callback for when uploading starts
            xhr.upload.onloadstart = function(event){
                var inLoadingIndex = $.inArray(fileItem, fileItemList)
                if (inLoadingIndex == -1){
                    // Item is not loading, add to inProgress queue
                    newLoadingItem = {
                        file: fileItem,
                        id: policyData.file_id,
                        order: fileItemList.length + 1
                    }
                    fileItemList.push(newLoadingItem)
                  }
                fileItem.xhr = xhr
            }

            // Monitor upload progress and attach to fileItem.
            xhr.upload.addEventListener("progress", function(event){
                if (event.lengthComputable) {
                 var progress = Math.round(event.loaded / event.total * 100);
                    fileItem.progress = progress
                    displayItems(fileItemList)
                }
            })

            xhr.upload.addEventListener("load", function(event){
                console.log("Complete")
                // handle FileItem Upload being complete.
                // fileUploadComplete(fileItem, policyData)

            })

            xhr.open('POST', policyData.url , true);
            xhr.send(fd);
        })
}});

我的代码与教程中的代码几乎相同,我只是复制粘贴它,它已经让我吃了三天了。 我尝试使用 CORS 标头,ive

CORS_ALLOW_ALL_ORIGINS = True CORS_ALLOW_CREDENTIALS = True

jquery django ajax amazon-web-services amazon-s3
1个回答
0
投票

我遇到了同样的问题,据我所知,在检查控制台后,我遇到了:

<?xml version="1.0" encoding="UTF-8"?>
<Error>
<Code>InvalidRequest</Code>
<Message>The authorization mechanism you have provided is not supported. Please use AWS4-HMAC-SHA256.</Message>
<RequestId>.../RequestId>
<HostId>...</HostId>
</Error>

这似乎与这篇文章有关:不支持您提供的授权机制。请使用AWS4-HMAC-SHA256

我相信这是您使用的区域需要 S4 签名的结果。我将详细说明我如何尽快自行解决问题,但希望这是一个开始!

© www.soinside.com 2019 - 2024. All rights reserved.