49

I am attempting to upload a file into a S3 bucket, but I don't have access to the root level of the bucket and I need to upload it to a certain prefix instead. The following code:

import boto3
s3 = boto3.resource('s3')
open('/tmp/hello.txt', 'w+').write('Hello, world!')
s3_client.upload_file('/tmp/hello.txt', bucket_name, prefix+'hello-remote.txt')

Gives me an error:

An error occurred (AccessDenied) when calling the PutObject operation: Access Denied: ClientError Traceback (most recent call last): File "/var/task/tracker.py", line 1009, in testHandler s3_client.upload_file('/tmp/hello.txt', bucket_name, prefix+'hello-remote.txt') File "/var/runtime/boto3/s3/inject.py", line 71, in upload_file extra_args=ExtraArgs, callback=Callback) File "/var/runtime/boto3/s3/transfer.py", line 641, in upload_file self._put_object(filename, bucket, key, callback, extra_args) File "/var/runtime/boto3/s3/transfer.py", line 651, in _put_object **extra_args) File "/var/runtime/botocore/client.py", line 228, in _api_call return self._make_api_call(operation_name, kwargs) File "/var/runtime/botocore/client.py", line 492, in _make_api_call raise ClientError(parsed_response, operation_name) ClientError: An error occurred (AccessDenied) when calling the PutObject operation: Access Denied

bucket_name is in the format abcd while prefix is in the format a/b/c/d/. I'm not sure if the error is due to the slashes being wrong or if there's some way you can specify the prefix elsewhere, or if I don't have write permissions (although I supposedly do).

This code executes without any errors:

for object in output_bucket.objects.filter(Prefix=prefix):
    print(object.key)

Although there is no output as the bucket is empty.

foxes
  • 808
  • 1
  • 7
  • 13
  • 3
    `s3_client` is not defined. – Hephaestus Oct 30 '18 at 18:50
  • 1
    I no longer have access to the code, but I would assume it was created using `s3_client = boto3.client('s3')` [taken from the docs](https://boto3.amazonaws.com/v1/documentation/api/latest/reference/services/s3.html#client) – foxes Nov 02 '18 at 22:46

6 Answers6

46

I'm assuming you have all this set up:

  1. AWS Access Key ID and Secret Key set up (typically stored at ~/.aws/credentials
  2. You have access to S3 and you know your bucket names & prefixes (subdirectories)

According to the Boto3 S3 upload_file documentation, you should upload your upload like this:

upload_file(Filename, Bucket, Key, ExtraArgs=None, Callback=None, Config=None)

import boto3
s3 = boto3.resource('s3')
s3.meta.client.upload_file('/tmp/hello.txt', 'mybucket', 'hello.txt')

The key to note here is s3.meta.client. Don't forget that.

ggorlen
  • 44,755
  • 7
  • 76
  • 106
John Adjei
  • 1,483
  • 2
  • 14
  • 19
  • 2
    I ran `s3_resource.meta.client.upload_file(PATH_IN_COMPUTER, BUCKET_NAME, KEY)` The code ran without errors but the file did not get uploaded. Any thoughts on what is going wrong? – Hari Apr 20 '21 at 09:45
21
import boto3

s3 = boto3.resource('s3')
s3.meta.client.upload_file( 'csv1.csv', "bucketname", "prefixna/csv1.csv")
Brian Tompsett - 汤莱恩
  • 5,753
  • 72
  • 57
  • 129
Ranajit kumar
  • 239
  • 2
  • 3
  • Seems the same as [this answer](https://stackoverflow.com/a/37017853/6243352). Can you clarify what significant aspect is being added here? Thanks. – ggorlen Mar 28 '23 at 01:30
15

Turns out I needed SSE:

import boto3
s3_client = boto3.client('s3', aws_access_key_id='YOUR_ACCESS_KEY', aws_secret_access_key='YOUR_SECRET')
transfer = S3Transfer(s3_client)
transfer.upload_file('/tmp/hello.txt', bucket_name, prefix+'hello-remote.txt', extra_args={'ServerSideEncryption': "AES256"})
Sahil
  • 1,387
  • 14
  • 41
foxes
  • 808
  • 1
  • 7
  • 13
  • 21
    What is `s3_client`? It is not defined anywhere. Neither is prefix. – pookie Mar 02 '17 at 15:23
  • @foxes - Thank you! I had completely forgot about the AES encryption, and was pulling my hair out wondering why it wasn't working! – Ryan G Oct 09 '17 at 18:26
14

Below is an alternative to John Adjei's answer. This is also taken from the Boto3 S3 upload_file documentation. Because the Client is low-level (low abstraction / closer to machine code) it can improve performance - especially if you a dealing with Big Data.

import boto3
s3 = boto3.client('s3')
with open("FILE_NAME", "rb") as f:
    s3.upload_fileobj(f, "BUCKET_NAME", "OBJECT_NAME")
Gwen Au
  • 859
  • 9
  • 10
  • 2
    I prefer the approach using `upload_fileobj()` as I am working with a filestream already vs. having access to the file path via the filesystem – Gibron Mar 09 '22 at 19:05
7

With resource

s3 = boto3.resource('s3')
s3.Bucket('mybucket').upload_file('/tmp/hello.txt', '/detination/s3/path/hello.txt')

with client

s3_client = boto3.client('s3')
s3_client.upload_file('/tmp/hello.txt', 'BUCKET_NAME', '/detination/s3/path/hello.txt',)
LoMaPh
  • 1,476
  • 2
  • 20
  • 33
6

Here is my answer:

import boto3

s3_client = boto3.client(service_name='s3', region_name='ap-southeast-1',
                         aws_access_key_id='AWS_ACCESS_KEY_ID',
                         aws_secret_access_key='AWS_SECRET_ACCESS_KEY')

dest_bucket = 'data-lake'
dest_prefix = 'datamart/my_file_name/'

file_name = 'my_file_name'+ '.parquet'

s3.meta.client.delete_object(Bucket=dest_bucket,Key=dest_prefix + file_name)
  • 2
    Welcome to StackOverflow. While this code may answer the question, providing additional context regarding *how* and/or *why* it solves the problem would improve the answer's long-term value. – Sven Eberth Jun 21 '21 at 11:23
  • @SvenEberth Thanks for your comment. I noted to help improve StackOverflow. – Long Nguyễn Hoàng Oct 27 '21 at 11:06