2011-08-19 1 views
4

저는 지금까지이 문제에 대한 많은 반복을 해왔고 많은 다른 예제들을 찾아 보았습니다.S3에 직접 업로드하기 Python/Boto/Django를 사용하여 정책을 구성하십시오.

Plupload (http://www.plupload.com/)와 AWS S3 직접 게시 방법 (http://aws.amazon.com/articles/1434)을 결합하려고합니다. 그러나 나는 전송을위한 정책 및 서명을 작성하는 방식에 문제가 있다고 생각합니다. 양식을 제출할 때 서버에서 응답을받지 못하지만 서버에 대한 연결이 재설정됩니다. 내가 예에서 파이썬 코드를 사용하여 시도

:

import base64 
import hmac, sha 

policy = base64.b64encode(policy_document) 

signature = base64.b64encode(
hmac.new(aws_secret_key, policy, sha).digest()) 

은 또한 파이썬에서 더 최신의 hashlib 라이브러리를 사용하는 것을 시도했다. 무엇이든 내 정책과 서명을 작성하기 위해서 사용하는 방법, 난 항상 여기에 생성 된 다른 값을 얻을 : 나는이 질문을 읽고

http://s3.amazonaws.com/doc/s3-example-code/post/post_sample.html

:

How do I make Plupload upload directly to Amazon S3?

하지만 난 발견 예는 지나치게 복잡하고 정확하게 구현할 수 없었습니다.

내 가장 최근의 시도는 BOTO 라이브러리의 일부를 사용하고 있습니다 :

http://boto.cloudhackers.com/ref/s3.html#module-boto.s3.connection

그러나 S3Commection.build_post_form_args 방법을 사용하여 어느 나를 위해 일하지 않았다.

누군가가 파이썬을 사용하여 게시 양식을 만드는 방법에 대한 적절한 예를 제공 할 수 있다면 매우 감사하게 생각합니다. 연결이 항상 재설정되는 이유에 대한 간단한 통찰력조차도 좋을 것입니다.

일부주의 사항 :

가능한 경우 hashlib을 사용하고 싶습니다. Amazon에서 XML 응답을 받고 싶습니다 (아마도 "success_action_status = '201'"). 최대 크기의 파일 (최대 크기 ~ 2GB)을 업로드 할 수 있어야합니다.

마지막으로 Chrome에서 실행하면 업로드 진행률이 향상되고 업로드는 일반적으로 약 37 % 실패합니다.

답변

3

나는 Boto를 사용해 보았지만 내가 원하는 모든 헤더를 넣지 않았다. 아래에서 정책, 서명 및 게시 양식 값 사전을 생성하기 위해 수행 할 작업을 볼 수 있습니다.

모든 x-amz-meta- * 태그는 사용자 정의 헤더 속성이므로 필요하지 않습니다. 또한 양식에 포함될 모든 것이 인코딩되고 서명 된 정책에 있어야 함을 주목하십시오.

def generate_post_form(bucket_name, key, post_key, file_id, file_name, content_type): 
    import hmac 
    from hashlib import sha1 
    from django.conf import settings 
    policy = """{"expiration": "%(expires)s","conditions": [{"bucket":"%(bucket)s"},["eq","$key","%(key)s"],{"acl":"private"},{"x-amz-meta-content_type":"%(content_type)s"},{"x-amz-meta-file_name":"%(file_name)s"},{"x-amz-meta-post_key":"%(post_key)s"},{"x-amz-meta-file_id":"%(file_id)s"},{"success_action_status":"200"}]}""" 
    policy = policy%{ 
    "expires":(datetime.utcnow()+settings.TIMEOUT).strftime("%Y-%m-%dT%H:%M:%SZ"), # This has to be formatted this way 
    "bucket": bucket_name, # the name of your bucket 
    "key": key, # this is the S3 key where the posted file will be stored 
    "post_key": post_key, # custom properties begin here 
    "file_id":file_id, 
    "file_name": file_name, 
    "content_type": content_type, 
    } 
    encoded = policy.encode('utf-8').encode('base64').replace("\n","") # Here we base64 encode a UTF-8 version of our policy. Make sure there are no new lines, Amazon doesn't like them. 
    return ("%s://%s.s3.amazonaws.com/"%(settings.HTTP_CONNECTION_TYPE, self.bucket_name), 
      {"policy":encoded, 
      "signature":hmac.new(settings.AWS_SECRET_KEY,encoded,sha1).digest().encode("base64").replace("\n",""), # Generate the policy signature using our Amazon Secret Key 
      "key": key, 
      "AWSAccessKeyId": settings.AWS_ACCESS_KEY, # Obviously the Amazon Access Key 
      "acl":"private", 
      "x-amz-meta-post_key":post_key, 
      "x-amz-meta-file_id":file_id, 
      "x-amz-meta-file_name": file_name, 
      "x-amz-meta-content_type": content_type, 
      "success_action_status":"200", 
      }) 

반환 튜플은 양식을 생성하는 데 사용할 수있는 이름이/ID가해야 숨겨진 필드와 사전에서 키 값 쌍과 실제 파일 입력 필드의 모든으로 생성 된 S3 URL로 게시물 "파일"이되어야합니다.

호프가 도움이 될 것입니다.

+0

settings.TIMEOUT의 유형은 무엇입니까? – MrOodles

+0

그것은 datetime.timedelta입니다 –

+0

이것은 지금까지 최고의 답변입니다. 거의 질문에 답하고, 나는이 코드를 수정하여 결국에는 작동하는 솔루션을 만들었습니다. 나는 그것을 여기 게시 할 것이지만 나는 그것에 대한 권리를 가지고 있지 않다. 나의 의뢰인이한다. 나는 그들과 이야기 할 것이고 내가 여기에 그것을 추가 할 수 있는지 알게 될 것이다. – MrOodles

0

나는 일 동안 거의 똑같은 코드를 사용하여,이 똑같은 문제로 어려움을 겪고 있었어요

1

이 업로드 직접 S3와 PLUPLOAD을 결합 https://github.com/burgalon/plupload-s3mixin 체크 아웃하십시오. (Python Generated Signature for S3 Post을 참조하십시오.) 방금 White Box Dev의 코드에 따라 정책을 인코딩하려했으나 AWS가 제안해야하는 것과 동일한 방식으로 나오지 않았습니다. 나는 결국 포기하고 사용 ...

http://s3.amazonaws.com/doc/s3-example-code/post/post_sample.html

... 그리고 그냥은 HTML 형태로 반환 값을 삽입했다. 훌륭하게 작동합니다.

@ Oodles : aws_secret_key를 별도의 파일에 저장하는 경우 bash 명령 ls -al을 사용하여 서명을 생성하기 전에 바이트 수를 확인하십시오. 40 바이트 길이 여야합니다. White Box Dev가 지적했듯이 AWS는 \ n을 좋아하지 않으므로 aws_secret_key 문자열을 숨길 때이 숨겨진 문자 (또는 캐리지 리턴 또는^M)를 묶어서 41 바이트 길이로 만들 수도 있습니다 . .replace ("\ n", "") 또는 .rstrip()을 사용하면 스크립트로 읽어 들여서 제거 할 수 있습니다. .encode ("utf-8")도 사용할 수 있습니다. 그러나 나를 위해 일한 사람은 없습니다. Windows 또는 Unix에서 Python을 실행하고 있는지 궁금하다면 ... 편집기에서 자동으로 삽입하지 않고 이맥스를 사용하여 문자열을 저장할 수도 있습니다.

3

네이선의 대답이 나를 도와주었습니다. 나는 현재 나를 위해 일하는 두 가지 해결책을 포함시켰다.

첫 번째 해결 방법은 일반 Python을 사용합니다. 두 번째는 boto를 사용합니다.

나는 boto가 먼저 작동하려고했지만 오류가 계속 발생했습니다. 그래서 아마존 루비 문서로 돌아가 S3가 boto없이 파이썬을 사용하여 파일을 받도록했습니다. (Browser Uploads to S3 using HTML POST)

무슨 일이 있었는지를 이해 한 후 오류를 수정하고 간단한 해결책 인 boto를 사용할 수있었습니다.

파이썬을 사용하여 정책 문서 및 서명을 설정하는 방법을 명시 적으로 보여주기 때문에 해결책 1이 포함됩니다.

목표는 성공적인 업로드 후 사용자가 볼 수있는 "성공"페이지와 함께 동적 페이지로 html 업로드 페이지를 만드는 것이 었습니다. 솔루션 1은 폼 업로드 페이지의 동적 생성을 보여주고 솔루션 2는 업로드 폼 페이지와 성공 페이지의 생성을 보여줍니다.

해결 방법 1 :

import base64 
import hmac, hashlib 

###### EDIT ONLY THE FOLLOWING ITEMS ###### 

DEBUG = 1 
AWS_SECRET_KEY = "MySecretKey" 
AWS_ACCESS_KEY = "MyAccessKey" 
HTML_NAME = "S3PostForm.html" 
EXPIRE_DATE = "2015-01-01T00:00:00Z" # Jan 1, 2015 gmt 
FILE_TO_UPLOAD = "${filename}" 
BUCKET = "media.mysite.com" 
KEY = "" 
ACL = "public-read" # or "private" 
SUCCESS = "http://media.mysite.com/success.html" 
CONTENT_TYPE = "" 
CONTENT_LENGTH = 1024**3 # One gigabyte 
HTTP_OR_HTTPS = "http" # Or "https" for better security 
PAGE_TITLE = "My Html Upload to S3 Form" 
ACTION = "%s://%s.s3.amazonaws.com/" % (HTTP_OR_HTTPS, BUCKET) 

###### DON'T EDIT FROM HERE ON DOWN ###### 

policy_document_data = { 
"expire": EXPIRE_DATE, 
"bucket_name": BUCKET, 
"key_name": KEY, 
"acl_name": ACL, 
"success_redirect": SUCCESS, 
"content_name": CONTENT_TYPE, 
"content_length": CONTENT_LENGTH, 
} 

policy_document = """ 
{"expiration": "%(expire)s", 
    "conditions": [ 
    {"bucket": "%(bucket_name)s"}, 
    ["starts-with", "$key", "%(key_name)s"], 
    {"acl": "%(acl_name)s"}, 
    {"success_action_redirect": "%(success_redirect)s"}, 
    ["starts-with", "$Content-Type", "%(content_name)s"], 
    ["content-length-range", 0, %(content_length)d] 
    ] 
} 
""" % policy_document_data 

policy = base64.b64encode(policy_document) 
signature = base64.b64encode(hmac.new(AWS_SECRET_KEY, policy, hashlib.sha1).digest()) 

html_page_data = { 
"page_title": PAGE_TITLE, 
"action_name": ACTION, 
"filename": FILE_TO_UPLOAD, 
"access_name": AWS_ACCESS_KEY, 
"acl_name": ACL, 
"redirect_name": SUCCESS, 
"policy_name": policy, 
"sig_name": signature, 
"content_name": CONTENT_TYPE, 
} 

html_page = """ 
<html> 
<head> 
    <title>%(page_title)s</title> 
    <meta http-equiv="Content-Type" content="text/html; charset=UTF-8" /> 
</head> 
<body> 
<form action="%(action_name)s" method="post" enctype="multipart/form-data"> 
    <input type="hidden" name="key" value="%(filename)s"> 
    <input type="hidden" name="AWSAccessKeyId" value="%(access_name)s"> 
    <input type="hidden" name="acl" value="%(acl_name)s"> 
    <input type="hidden" name="success_action_redirect" value="%(redirect_name)s"> 
    <input type="hidden" name="policy" value="%(policy_name)s"> 
    <input type="hidden" name="signature" value="%(sig_name)s"> 
    <input type="hidden" name="Content-Type" value="%(content_name)s"> 

    <!-- Include any additional input fields here --> 

    Browse to locate the file to upload:<br \> <br \> 

    <input name="file" type="file"><br> <br \> 
    <input type="submit" value="Upload File to S3"> 
</form> 
</body> 
</html> 
""" % html_page_data 

with open(HTML_NAME, "wb") as f: 
    f.write(html_page) 

###### Dump output if testing ###### 
if DEBUG: 

    if 1: # Set true if not using the LEO editor 
     class G: 
      def es(self, data):print(data) 
     g = G() 

    items = [ 
    "", 
    "", 
    "policy_document: %s" % policy_document, 
    "ploicy: %s" % policy, 
    "signature: %s" % signature, 
    "", 
    "", 
    ] 
    for item in items: 
     g.es(item) 

해결 방법 2 :

from boto.s3 import connection 

###### EDIT ONLY THE FOLLOWING ITEMS ###### 

DEBUG = 1 
AWS_SECRET_KEY = "MySecretKey" 
AWS_ACCESS_KEY = "MyAccessKey" 
HTML_NAME = "S3PostForm.html" 
SUCCESS_NAME = "success.html" 
EXPIRES = 60*60*24*356 # seconds = 1 year 
BUCKET = "media.mysite.com" 
KEY = "${filename}" # will match file entered by user 
ACL = "public-read" # or "private" 
SUCCESS = "http://media.mysite.com/success.html" 
CONTENT_TYPE = "" # seems to work this way 
CONTENT_LENGTH = 1024**3 # One gigabyte 
HTTP_OR_HTTPS = "http" # Or https for better security 
PAGE_TITLE = "My Html Upload to S3 Form" 

###### DON'T EDIT FROM HERE ON DOWN ###### 

conn = connection.S3Connection(AWS_ACCESS_KEY,AWS_SECRET_KEY) 
args = conn.build_post_form_args(
    BUCKET, 
    KEY, 
    expires_in=EXPIRES, 
    acl=ACL, 
    success_action_redirect=SUCCESS, 
    max_content_length=CONTENT_LENGTH, 
    http_method=HTTP_OR_HTTPS, 
    fields=None, 
    conditions=None, 
    storage_class='STANDARD', 
    server_side_encryption=None, 
    ) 

form_fields = "" 
line = ' <input type="hidden" name="%s" value="%s" >\n' 
for item in args['fields']: 
    new_line = line % (item["name"], item["value"]) 
    form_fields += new_line 

html_page_data = { 
"page_title": PAGE_TITLE, 
"action": args["action"], 
"input_fields": form_fields, 
} 

html_page = """ 
<html> 
<head> 
    <title>%(page_title)s</title> 
    <meta http-equiv="Content-Type" content="text/html; charset=UTF-8" /> 
</head> 
<body> 
<form action="%(action)s" method="post" enctype="multipart/form-data" > 
%(input_fields)s 
    <!-- Include any additional input fields here --> 

    Browse to locate the file to upload:<br \> <br \> 

    <input name="file" type="file"><br> <br \> 
    <input type="submit" value="Upload File to S3"> 
</form> 
</body> 
</html> 
""" % html_page_data 

with open(HTML_NAME, "wb") as f: 
    f.write(html_page) 

success_page = """ 
<html> 
    <head> 
    <title>S3 POST Success Page</title> 
     <meta http-equiv="Content-Type" content="text/html; charset=UTF-8" /> 
     <script src="jquery.js"></script> 
     <script src="purl.js"></script> 
<!-- 

    Amazon S3 passes three data items in the url of this page if 
     the upload was successful: 
     bucket = bucket name 
     key = file name upload to the bucket 
     etag = hash of file 

    The following script parses these values and puts them in 
    the page to be displayed. 

--> 

<script type="text/javascript"> 
var pname,url,val,params=["bucket","key","etag"]; 
$(document).ready(function() 
{ 
    url = $.url(); 
    for (param in params) 
    { 
    pname = params[param]; 
    val = url.param(pname); 
    if(typeof val != 'undefined') 
     document.getElementById(pname).value = val; 
    } 
}); 
</script> 

    </head> 
    <body> 
     <div style="margin:0 auto;text-align:center;"> 
     <p>Congratulations!</p> 
     <p>You have successfully uploaded the file.</p> 
     <form action="#" method="get" 
      >Location: 
     <br /> 
      <input type="text" name="bucket" id="bucket" /> 
     <br />File Name: 
     <br /> 
      <input type="text" name="key" id="key" /> 
     <br />Hash: 
     <br /> 
      <input type="text" name="etag" id="etag" /> 
     </form> 
    </div> 
    </body> 
</html> 
""" 

with open(SUCCESS_NAME, "wb") as f: 
    f.write(success_page) 

###### Dump output if testing ###### 
if DEBUG: 

    if 1: # Set true if not using the LEO editor 
     class G: 
      def es(self, data):print(data) 
     g = G() 

    g.es("conn = %s" % conn) 
    for key in args.keys(): 
     if key is not "fields": 
      g.es("%s: %s" % (key, args[key])) 
      continue 
     for item in args['fields']: 
      g.es(item)