Python 使用fineuploader和django将文件上载到S3时出现签名错误消息

Python 使用fineuploader和django将文件上载到S3时出现签名错误消息,python,django,amazon-s3,fine-uploader,Python,Django,Amazon S3,Fine Uploader,更新:请参阅此消息底部的。这是python3/hmac版本的问题 我正在用fine uploader、S3和django 1.11建立一个文件上传系统。 我设置了URL、模板和视图,但以下是我在尝试上载文件时收到的(客户端)错误消息: [Fine Uploader 5.15.0]0的POST请求失败-响应代码400 [Fine Uploader 5.15.0]尝试分析签名响应时出错:语法错误:JSON分析错误:意外标识符“无效” [Fine Uploader 5.15.0]收到来自服务器的空响

更新:请参阅此消息底部的。这是python3/hmac版本的问题

我正在用fine uploader、S3和django 1.11建立一个文件上传系统。 我设置了URL、模板和视图,但以下是我在尝试上载文件时收到的(客户端)错误消息:

  • [Fine Uploader 5.15.0]0的POST请求失败-响应代码400
  • [Fine Uploader 5.15.0]尝试分析签名响应时出错:语法错误:JSON分析错误:意外标识符“无效”
  • [Fine Uploader 5.15.0]收到来自服务器的空响应或无效响应
  • [Fine Uploader 5.15.0]策略签名失败。从服务器收到一个空的或无效的响应
在django设置中,精细上载程序需要:

  • AWS\u客户端\u密钥\u密钥=IAM用户密钥访问密钥
  • AWS_服务器_公共_密钥=IAM用户访问密钥ID
  • AWS\u服务器\u密钥\u密钥=IAM用户密钥访问密钥
我从我创建的iam用户那里获得了我的访问密钥ID秘密访问密钥,并如上所示进行了设置。AWS\u客户端\u密钥\u密钥=AWS\u服务器\u密钥\u密钥=IAM用户密钥。我不确定这是否正确,这很可能是问题所在,但我不知道AWS_CLIENT_SECRET_KEY和AWS_SERVER_SECRET_KEY之间有什么区别,如果不是iam SECRET KEY,在哪里可以找到它们

代码如下:

视图:

背景

# Amazon variables. Be wary and don't hard-code your secret keys here. Rather,
# set them as environment variables, or read them from a file somehow.
AWS_CLIENT_SECRET_KEY = 'WDq/cy*****'
AWS_SERVER_PUBLIC_KEY = 'AK*****'
AWS_SERVER_SECRET_KEY = 'WDq/cy*****'

AWS_EXPECTED_BUCKET = 'mybucketname'
AWS_MAX_SIZE = 15000000
Cors政策

这似乎不是aws端设置的问题,因为我可以通过其他方式将文件放入我的存储桶

<?xml version="1.0" encoding="UTF-8"?>
<CORSConfiguration xmlns="http://s3.amazonaws.com/doc/2006-03-01/">
<CORSRule>
    <AllowedOrigin>*</AllowedOrigin>
    <AllowedMethod>GET</AllowedMethod>
    <AllowedMethod>HEAD</AllowedMethod>
    <AllowedMethod>PUT</AllowedMethod>
    <AllowedMethod>POST</AllowedMethod>
    <MaxAgeSeconds>3000</MaxAgeSeconds>
    <ExposeHeader>ETag</ExposeHeader>
    <AllowedHeader>*</AllowedHeader>
</CORSRule>
</CORSConfiguration>
使用Python2.7设置一个venv修复了这个问题,我使所有过程都正常工作。我正在调查这个问题,如果有人有问题,请告诉我


以下是视图代码,已完全更新,可用于python 3和Boto3。 你可能需要跑步

/Applications/Python\ 3.6/Install\ Certificates.command 
如果您使用的是Python3.6,并且在删除文件时遇到ssl错误问题,请使用mac命令行

所以这不是aws或许可问题。。。但是python3字节/字符串的问题。 此行还存在类型强制问题,该问题始终返回false:

    return bucket == settings.AWS_EXPECTED_BUCKET and parsed_max_size == settings.AWS_MAX_SIZE
阻止显示任何有意义的错误消息

from django.conf import settings
from django.http import HttpResponse
from django.shortcuts import render
from django.views.decorators.csrf import csrf_exempt

import base64, hmac, hashlib, json

import boto3

# Enforce session to inject credentials
session = boto3.Session(
        aws_access_key_id = settings.AWS_SERVER_PUBLIC_KEY,
        aws_secret_access_key = settings.AWS_SERVER_SECRET_KEY,
)
S3 = session.resource( 's3' )


def video_create_form( request ):
    return render( request, 'video_create_form_view.html' )


@csrf_exempt
def success_redirect_endpoint( request ):
    """ This is where the upload will send a POST request after the
    file has been stored in S3.
    """
    return make_response( 200 )


@csrf_exempt
def handle_s3( request ):
    """ View which handles all POST and DELETE requests sent by Fine Uploader
    S3. You will need to adjust these paths/conditions based on your setup.
    """
    if request.method == "POST":
        return handle_POST( request )
    elif request.method == "DELETE":
        return handle_DELETE( request )
    else:
        return HttpResponse( status = 405 )


def handle_POST( request ):
    """ Handle S3 uploader POST requests here. For files <=5MiB this is a simple
    request to sign the policy document. For files >5MiB this is a request
    to sign the headers to start a multipart encoded request.
    """
    class MyEncoder( json.JSONEncoder ):
        """Converts a dict of bytes to Json"""
        def default( self, obj ):
            if isinstance( obj, (bytes, bytearray) ):
                return obj.decode( "ASCII" )  # <- or any other encoding of your choice
            # Let the base class default method raise the TypeError
            return json.JSONEncoder.default( self, obj )

    if request.POST.get( 'success', None ):
        return make_response( 200 )
    else:
        request_payload = json.loads( request.body )
        headers = request_payload.get( 'headers', None )
        if headers:
            # The presence of the 'headers' property in the request payload
            # means this is a request to sign a REST/multipart request
            # and NOT a policy document
            response_data = sign_headers( headers )
        else:
            if not is_valid_policy( request_payload ):
                return make_response( 400, { 'invalid': True } )
            response_data = sign_policy_document( request_payload )
        response_payload = json.dumps( response_data, cls = MyEncoder )
        return make_response( 200, response_payload )


def handle_DELETE( request ):
    """ Handle file deletion requests. For this, we use the Amazon Python SDK, boto.
    """
    if boto3:
        bucket_name = request.GET.get( 'bucket' )
        key_name = request.GET.get( 'key' )
        S3.Object( bucket_name, key_name ).delete()

        return make_response( 200 )
    else:
        return make_response( 500 )


def make_response( status = 200, content = None ):
    """ Construct an HTTP response. Fine Uploader expects 'application/json'.
    """
    response = HttpResponse()
    response.status_code = status
    response[ 'Content-Type' ] = "application/json"
    response.content = content
    return response


def is_valid_policy( policy_document ):
    """ Verify the policy document has not been tampered with client-side
    before sending it off.
    """
    # bucket = settings.AWS_EXPECTED_BUCKET
    # parsed_max_size = settings.AWS_MAX_SIZE
    bucket = ''
    parsed_max_size = 0

    for condition in policy_document[ 'conditions' ]:
        if isinstance( condition, list ) and condition[ 0 ] == 'content-length-range':
            parsed_max_size = condition[ 2 ]
        else:
            if condition.get( 'bucket', None ):
                bucket = condition[ 'bucket' ]

    return bucket == settings.AWS_EXPECTED_BUCKET and int(
            parsed_max_size ) == settings.AWS_MAX_SIZE


def sign_policy_document( policy_document ):
    """ Sign and return the policy doucument for a simple upload.
    http://aws.amazon.com/articles/1434/#signyours3postform
    """
    policy_document_string = str.encode( str( policy_document ) )
    policy = base64.b64encode( policy_document_string )
    aws_secret_key = settings.AWS_CLIENT_SECRET_KEY
    secret_key = str.encode( aws_secret_key )

    signature = base64.b64encode(
            hmac.new( secret_key, policy, hashlib.sha1 ).digest() )
    return {
        'policy'   : policy,
        'signature': signature
    }


def sign_headers( headers ):
    """ Sign and return the headers for a chunked upload. """
    headers_bytes = bytearray( headers, 'utf-8' )  # hmac doesn't want unicode
    aws_client_secret = str.encode( settings.AWS_CLIENT_SECRET_KEY )
    return {
        'signature': base64.b64encode(
                hmac.new( aws_client_secret, headers_bytes, hashlib.sha1 ).digest() )
    }
来自django.conf导入设置的

从django.http导入HttpResponse
从django.shortcuts导入渲染
从django.views.decorators.csrf导入csrf_豁免
导入base64、hmac、hashlib、json
进口boto3
#强制会话以插入凭据
session=bot3.session(
aws\u访问\u密钥\u id=settings.aws\u服务器\u公钥,
aws\u secret\u access\u key=settings.aws\u SERVER\u secret\u key,
)
S3=会话资源('S3')
def视频创建表单(请求):
返回渲染(请求“video\u create\u form\u view.html”)
@豁免
def成功_重定向_终结点(请求):
“”“这是上载将在
文件已存储在S3中。
"""
返回make_响应(200)
@豁免
def句柄_s3(请求):
“”“处理Fine Uploader发送的所有POST和DELETE请求的视图”
S3.您需要根据设置调整这些路径/条件。
"""
如果request.method==“POST”:
返回句柄\u POST(请求)
elif request.method==“删除”:
返回句柄\u删除(请求)
其他:
返回HttpResponse(状态=405)
def句柄_POST(请求):
“”“在此处处理S3上传程序POST请求。对于文件5MiB,这是一个请求。”
签署标头以启动多部分编码的请求。
"""
类MyEncoder(json.JSONEncoder):
“”“将字节的dict转换为Json”“”
def默认值(自身、obj):
如果isinstance(obj,(字节,字节数组)):

返回obj.decode(“ASCII”)#400状态码通常表示发送的数据无效。在浏览器开发面板中搜索请求中发送的数据。这是一个python版本问题,请查看我的更新帖子很高兴您找到了解决方案^嗨,罗伯特,您找到AWS_客户端_密钥和AWS_服务器_密钥之间的区别了吗?我在这两种情况下使用相同的密钥。
 raise TypeError("key: expected bytes or bytearray, but got %r" % type(key).__name__)
TypeError: key: expected bytes or bytearray, but got 'str
/Applications/Python\ 3.6/Install\ Certificates.command 
    return bucket == settings.AWS_EXPECTED_BUCKET and parsed_max_size == settings.AWS_MAX_SIZE
from django.conf import settings
from django.http import HttpResponse
from django.shortcuts import render
from django.views.decorators.csrf import csrf_exempt

import base64, hmac, hashlib, json

import boto3

# Enforce session to inject credentials
session = boto3.Session(
        aws_access_key_id = settings.AWS_SERVER_PUBLIC_KEY,
        aws_secret_access_key = settings.AWS_SERVER_SECRET_KEY,
)
S3 = session.resource( 's3' )


def video_create_form( request ):
    return render( request, 'video_create_form_view.html' )


@csrf_exempt
def success_redirect_endpoint( request ):
    """ This is where the upload will send a POST request after the
    file has been stored in S3.
    """
    return make_response( 200 )


@csrf_exempt
def handle_s3( request ):
    """ View which handles all POST and DELETE requests sent by Fine Uploader
    S3. You will need to adjust these paths/conditions based on your setup.
    """
    if request.method == "POST":
        return handle_POST( request )
    elif request.method == "DELETE":
        return handle_DELETE( request )
    else:
        return HttpResponse( status = 405 )


def handle_POST( request ):
    """ Handle S3 uploader POST requests here. For files <=5MiB this is a simple
    request to sign the policy document. For files >5MiB this is a request
    to sign the headers to start a multipart encoded request.
    """
    class MyEncoder( json.JSONEncoder ):
        """Converts a dict of bytes to Json"""
        def default( self, obj ):
            if isinstance( obj, (bytes, bytearray) ):
                return obj.decode( "ASCII" )  # <- or any other encoding of your choice
            # Let the base class default method raise the TypeError
            return json.JSONEncoder.default( self, obj )

    if request.POST.get( 'success', None ):
        return make_response( 200 )
    else:
        request_payload = json.loads( request.body )
        headers = request_payload.get( 'headers', None )
        if headers:
            # The presence of the 'headers' property in the request payload
            # means this is a request to sign a REST/multipart request
            # and NOT a policy document
            response_data = sign_headers( headers )
        else:
            if not is_valid_policy( request_payload ):
                return make_response( 400, { 'invalid': True } )
            response_data = sign_policy_document( request_payload )
        response_payload = json.dumps( response_data, cls = MyEncoder )
        return make_response( 200, response_payload )


def handle_DELETE( request ):
    """ Handle file deletion requests. For this, we use the Amazon Python SDK, boto.
    """
    if boto3:
        bucket_name = request.GET.get( 'bucket' )
        key_name = request.GET.get( 'key' )
        S3.Object( bucket_name, key_name ).delete()

        return make_response( 200 )
    else:
        return make_response( 500 )


def make_response( status = 200, content = None ):
    """ Construct an HTTP response. Fine Uploader expects 'application/json'.
    """
    response = HttpResponse()
    response.status_code = status
    response[ 'Content-Type' ] = "application/json"
    response.content = content
    return response


def is_valid_policy( policy_document ):
    """ Verify the policy document has not been tampered with client-side
    before sending it off.
    """
    # bucket = settings.AWS_EXPECTED_BUCKET
    # parsed_max_size = settings.AWS_MAX_SIZE
    bucket = ''
    parsed_max_size = 0

    for condition in policy_document[ 'conditions' ]:
        if isinstance( condition, list ) and condition[ 0 ] == 'content-length-range':
            parsed_max_size = condition[ 2 ]
        else:
            if condition.get( 'bucket', None ):
                bucket = condition[ 'bucket' ]

    return bucket == settings.AWS_EXPECTED_BUCKET and int(
            parsed_max_size ) == settings.AWS_MAX_SIZE


def sign_policy_document( policy_document ):
    """ Sign and return the policy doucument for a simple upload.
    http://aws.amazon.com/articles/1434/#signyours3postform
    """
    policy_document_string = str.encode( str( policy_document ) )
    policy = base64.b64encode( policy_document_string )
    aws_secret_key = settings.AWS_CLIENT_SECRET_KEY
    secret_key = str.encode( aws_secret_key )

    signature = base64.b64encode(
            hmac.new( secret_key, policy, hashlib.sha1 ).digest() )
    return {
        'policy'   : policy,
        'signature': signature
    }


def sign_headers( headers ):
    """ Sign and return the headers for a chunked upload. """
    headers_bytes = bytearray( headers, 'utf-8' )  # hmac doesn't want unicode
    aws_client_secret = str.encode( settings.AWS_CLIENT_SECRET_KEY )
    return {
        'signature': base64.b64encode(
                hmac.new( aws_client_secret, headers_bytes, hashlib.sha1 ).digest() )
    }