使用Django + Fineuploader + boto 从S3删除文件
我在使用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/*"
}
]
}