2012-04-06 32 views
19

Sabía cómo descargar archivos de esta manera: key.generate_url (3600).Cómo generar una url temporal para subir archivos a Amazon S3 con la biblioteca de boto?

Pero cuando traté de cargar: key.generate_url (3600, method = 'PUT'), la url no funcionó. Me dijeron: "La firma de solicitud que calculamos no coincide con la firma que proporcionó. Verifique su clave y el método de firma".

No encuentro el código de ejemplo en la página de inicio de boto para saber cómo usar la función generate_url (method = 'PUT'). ¿Alguien aquí sabe cómo usarlo para la carga? cómo configurar los parámetros para la ruta del archivo de carga?

+0

es que un nuevo archivo? Para crear un archivo nuevo, debe usar 'POST', no' PUT' – vartec

+0

@vartec: ¿Qué quiere decir "un nuevo archivo"? En mi caso de uso, a veces tengo que cargar una nueva clave en un cubo determinado, a veces tengo que sobrescribir la clave anterior. Así que creo que necesito el ejemplo de código para 'PUT' y 'POST'. –

Respuesta

40

Encontré algo de tiempo para experimentar con esto y esto es lo que encontré.

>>> import boto 
>>> c =boto.connect_s3() 
>>> fp = open('myfiletoupload.txt') 
>>> content_length = len(fp.read()) 
>>> c.generate_url(300, 'PUT', 'test-1332789015', 'foobar', headers={'Content-Length': str(content_length)}, force_http=True) 
'http://test-1332789015.s3.amazonaws.com/foobar?Signature=oUARG45mR95utXsiQYRJNiCI4x4%3D&Expires=1333731456&AWSAccessKeyId=AKIAJOTCCJRP4C3NSMYA&Content-Length=16' 

Yo era entonces capaz de usar curl para poner el archivo a esa URL como esta:

$ curl --request PUT --upload-file myfiletoupload.txt "http://test-1332789015.s3.amazonaws.com/foobar?Signature=oUARG45mR95utXsiQYRJNiCI4x4%3D&Expires=1333731456&AWSAccessKeyId=AKIAJOTCCJRP4C3NSMYA&Content-Length=16" 

Esto resultó en el archivo que se está cargado en el cubo. Entonces, parece que es posible. Es posible que desee ver si puede calcular el valor de content-md5 e incluirlo en los encabezados, pero también debe averiguar cómo obtener curl para enviar ese encabezado también. Además, debería poder hacer que esto funcione a través de HTTPS en lugar de HTTP, pero no lo he intentado.

+0

¡Funciona, muchas gracias! –

+2

El argumento 'headers' en esta respuesta no funcionará ahora, causará un error de 'falta de coincidencia de firma' cuando cargue. Si desea especificar encabezados * en la respuesta * cuando RECIBE el archivo más adelante, debe usar 'response_headers' y usar los campos tales como' response-content-type', etc. – MLister

+0

Probado en Google Cloud Storage pero fallido. ¿Le importaría echarle un vistazo a esta pregunta? Gracias. http://stackoverflow.com/questions/38938203/django-heroku-boto-direct-file-upload-to-google-cloud-storage –

8

Este es un seguimiento de la respuesta de garnaat del 6 de abril de 12.

Estoy generando un lado del servidor de URL firmado, donde tengo credenciales, y lo paso a un cliente para que un cliente pueda cargar contenido directamente. Confío en el cliente lo suficiente como para permitirle cargar archivos de tamaño arbitrario, pero no lo suficiente como para darle tokens de seguridad. Quería evitar que el cliente le dijera al servidor qué tan grande sería su contenido como parte de la solicitud. De ahí mi respuesta de seguimiento.

Pude obtener la URL firmada para que el método PUT funcionara sin especificar la longitud del contenido en los encabezados o especificando force_http = True.

Usando Boto 2.31.1: como en answere de garnaat:

>>> import boto 
>>> c =boto.connect_s3() 

entonces en lugar de eso utiliza:

>>> temp_url = c.generate_url(seconds_available, 'PUT', bucket_name, s3_key) 

Esto produjo una dirección URL en la forma siguiente:

https://s3_location/bucket_name/s3_key?Signature=Ew407JMktSIcFln%2FZe00VroCmTU%3D&Expires=1405647669&AWSAccessKeyId=kM__pEQo2AEVd_Juz4Qq 

Pude usar curl para publicar un archivo:

>>> os.system('curl --request PUT --upload-file true_measure/test_files/test_file_w_content.txt "'+temp_url+'"') 

Tuve un momento muy difícil para descifrar esto porque usualmente uso python requests para escribir pruebas y depurar; sin embargo, recibo un error de autenticación cuando trato de usarlo para poner un archivo en uno de estos urls firmados generados por boto usando solicitudes. No he depurado por completo esto, pero sospecho que es porque las solicitudes están agregando algunos encabezados adicionales en comparación con lo que curl produce.

Espero que esta respuesta de seguimiento le ahorre a alguien más el dolor de depuración que sufrí.

17

Esto es lo que parece en boto3 (probado con la versión 1.2.3).

En primer lugar, crear una URL presigned con s3.generate_presigned_url método:

>>> import boto3 
>>> s3 = boto3.client('s3') 
>>> s3.generate_presigned_url('put_object', Params={'Bucket':'YourBucket', 'Key':'YourKey'}, ExpiresIn=3600, HttpMethod='PUT') 
u'https://s3-ap-northeast-1.amazonaws.com/YourBucket/YourKey?AWSAccessKeyId=AKIAXXXXXXXXXXXXXXXX&Expires=1451061671&Signature=%2FtyAyCd5vrp13p%2FqLdoPkox7yTM%3D' 

PUT a S3 con una URL presigned

$ curl \ 
    --request PUT \ 
    --upload-file path/to/file \ 
    "https://s3-ap-northeast-1.amazonaws.com/YourBucket/YourKey?AWSAccessKeyId=AKIAXXXXXXXXXXXXXXXX&Expires=1451061671&Signature=%2FtyAyCd5vrp13p%2FqLdoPkox7yTM%3D" 
+0

cómo agrega sus credenciales, es decir, AWS_ACCESS_KEY_ID, AWS_SECRET_ACCESS_KEY? – CpILL

+1

@CpILL Puede pasar credenciales a través de variables de entorno. Por ejemplo, $ export AWS_ACCESS_KEY_ID = AKIAIOSFODNN7EXAMPLE http://docs.aws.amazon.com/cli/latest/userguide/cli-chap-getting-started.html#cli-environment – quiver

+0

¡excelente respuesta, gracias! –

9

Todas las otras respuestas asumen el archivo se cargará con curl, que es realmente no es conveniente en la mayoría de los scripts de Python. A continuación, una URL pre-firmado se genera con boto3 y el archivo se carga con el requests biblioteca:

session = boto3.Session(aws_access_key_id="XXX", aws_secret_access_key="XXX") 
s3client = session.client('s3') 
url = s3client.generate_presigned_url('put_object', Params={'Bucket': 'mybucket', 'Key': 'mykey'}) 

requests.put(url, data=open("/path/to/file").read()) 
Cuestiones relacionadas