使用Django + Fineuploader + boto 从S3删除文件

5 投票
1 回答
1057 浏览
提问于 2025-04-18 03:02

我在使用Fineuploader和Django/boto从S3删除文件时遇到了问题。我可以顺利地用Fineuploader上传文件到S3,并且能获取和显示图片的链接,但删除文件却一直不成功。

从boto的调试日志来看,似乎boto没有把令牌发送到S3请求中,我觉得这可能是我遇到的问题所在。

首先,我有boto的调试输出,因为我怀疑有更熟悉这个的人看了之后能帮我,但我会在后面提供我的完整设置(尽量按照这个链接的示例来做)。

删除时的终端输出

bucket_name:  XXXXXXXX
key_name: b45069b8-dc44-45fe-8b67-b25fc088bdea.jpg
aws_bucket:  <Bucket: XXXXXXXXX>
aws_key:  <Key: XXXXXXXX,b45069b8-dc44-45fe-8b67-b25fc088bdea.jpg>
2014-04-17 15:01:56,576 boto [DEBUG]:path=/b45069b8-dc44-45fe-8b67-b25fc088bdea.jpg
2014-04-17 15:01:56,577 boto [DEBUG]:auth_path=/thisorthis/b45069b8-dc44-45fe-8b67-b25fc088bdea.jpg
2014-04-17 15:01:56,577 boto [DEBUG]:Method: DELETE
2014-04-17 15:01:56,577 boto [DEBUG]:Path: /b45069b8-dc44-45fe-8b67-b25fc088bdea.jpg
2014-04-17 15:01:56,577 boto [DEBUG]:Data:
2014-04-17 15:01:56,577 boto [DEBUG]:Headers: {}
2014-04-17 15:01:56,577 boto [DEBUG]:Host: XXXXXXX.s3.amazonaws.com
2014-04-17 15:01:56,578 boto [DEBUG]:Port: 443
2014-04-17 15:01:56,578 boto [DEBUG]:Params: {}
2014-04-17 15:01:56,578 boto [DEBUG]:establishing HTTPS connection: host=thisorthis.s3.amazonaws.com, kwargs={'port': 443, 'timeout': 70}
2014-04-17 15:01:56,578 boto [DEBUG]:Token: None
2014-04-17 15:01:56,578 boto [DEBUG]:StringToSign:
DELETE


Thu, 17 Apr 2014 15:01:56 GMT
/XXXXXXXX/b45069b8-dc44-45fe-8b67-b25fc088bdea.jpg
2014-04-17 15:01:56,579 boto [DEBUG]:Signature:
AWS AKIAJYS27FQSNHPH3CXQ:dVKlBpulsY9LrOtHOa+xQmurIEM=
[17/Apr/2014 15:01:57] "DELETE /s3/delete/b45069b8-dc44-45fe-8b67-b25fc088bdea?key=b45069b8-dc44-45fe-8b67-b25fc088bdea.jpg&bucket=XXXXXXXX HTTP/1.1" 500 15975

settings.py:

AWS_CLIENT_SECRET_KEY = os.getenv("AWS_CLIENT_SECRET_KEY") 
AWS_SERVER_PUBLIC_KEY = os.getenv("AWS_SERVER_PUBLIC_KEY")
AWS_SERVER_SECRET_KEY = os.getenv("AWS_SERVER_SECRET_KEY")

AWS_EXPECTED_BUCKET = 'mybucketname'
AWS_MAX_SIZE = 15000000

显然,我在这里有我实际的存储桶名称,正如我所说,上传是正常的,所以我认为问题不在于设置。

Fineuploader实例

  $("#fine-uploader).fineUploaderS3({
        debug: true,
        request: {
            endpoint: 'XXXXX',
            accessKey: 'XXXXXXXX'
        },

        template: "simple-previews-template",

        signature: {
            endpoint: '/s3/signature/'
        },
        uploadSuccess: {
            endpoint: '/s3/success/'
        },

        iframeSupport: {
            localBlankPagePath: '/success.html'
        },

        deleteFile: {
            enabled: true,
            endpoint: '/s3/delete/'
        },

        classes: {
           dropActive: "cssClassToAddToDropZoneOnEnter"
        },

    }) 

urls.py

url(r'^s3/signature/', views.handle_s3, name="s3_signee"),
url(r'^s3/delete/', views.handle_s3, name='s3_delete'),
url(r'^s3/success/', views.success_redirect_endpoint, name="s3_succes_endpoint")

views.py

try:
    import boto
    from boto.s3.connection import Key, S3Connection
    boto.set_stream_logger('boto')
    S3 = S3Connection(development.AWS_SERVER_PUBLIC_KEY, development.AWS_SERVER_SECRET_KEY)
except ImportError, e:
    print("Could not import boto, the Amazon SDK for Python.")
    print("Deleting files will not work.")
    print("Install boto with")
    print("$ pip install boto")



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

    key = request.POST.get('key')
    response = {}
    response['url'] = key
    return HttpResponse(json.dumps(response), content_type="application/json")


@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.
    """

    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:
            print "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:
            print "no headers"
            if not is_valid_policy(request_payload):
                print "is not valid"
                return make_response(400, {'invalid': True})

            response_data = sign_policy_document(request_payload)
        response_payload = json.dumps(response_data)
        return make_response(200, response_payload)

def handle_DELETE(request):
    """ Handle file deletion requests. For this, we use the Amazon Python SDK,
    boto.
    """
    print "handle delete"
    if boto:
        bucket_name = request.REQUEST.get('bucket')
        print "bucket_name: ", bucket_name
        key_name = request.REQUEST.get('key')
        print "key_name:", key_name
        aws_bucket = S3.get_bucket(bucket_name, validate=False)
        print "aws_bucket: ", aws_bucket
        aws_key = Key(aws_bucket, key_name)
        print "aws_key: ", aws_key
        aws_key.delete()
        print "after aws_key.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 = development.AWS_EXPECTED_BUCKET
    parsed_max_size = development.AWS_MAX_SIZE
    print "check validity"

    # 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 == development.AWS_EXPECTED_BUCKET and parsed_max_size == development.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 = base64.b64encode(json.dumps(policy_document))
    signature = base64.b64encode(hmac.new(development.AWS_CLIENT_SECRET_KEY, policy, hashlib.sha1).digest())
    return {
        'policy': policy,
        'signature': signature
    }

def sign_headers(headers):
    """ Sign and return the headers for a chunked upload. """
    print "sign headers"
    return {
        'signature': base64.b64encode(hmac.new(development.AWS_CLIENT_SECRET_KEY, headers, hashlib.sha1).digest())
    }

1 个回答

2

原来我没有正确设置我的S3存储桶的权限,导致我的服务器无法发送删除请求,所以存储桶的日志显示了一个错误代码204。我已经允许了上传(PUT)和获取(GET)请求,所以上传和下载都能正常工作,但删除请求却不行。我把我的存储桶权限改成了更合适的样子:

{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Effect": "Allow",
      "Action": ["s3:ListAllMyBuckets"],
      "Resource": "arn:aws:s3:::*"
    },
    {
      "Effect": "Allow",
      "Action": [
        "s3:ListBucket",
        "s3:GetBucketLocation"
      ],
      "Resource": "arn:aws:s3:::xxxxx"
    },
    {
      "Effect": "Allow",
      "Action": [
        "s3:PutObject",
        "s3:GetObject",
        "s3:DeleteObject"
      ],
      "Resource": "arn:aws:s3:::xxxxx/*"
    }
  ]
}

撰写回答