Python Boto3 put_object file from lambda in s3 - python-3.x

I would like to send a json file in s3 from a lambda. I saw in the documentation that we can send with the function boto3 put_object a file or a bytes object (Body=b'bytes'|file).
But if I'm not wrong, if I send a file in s3 with Body=bytes and then I download my file the content will be not visible.
So in my lambda function, I receive messages from a SQS Queue, I created a file with the message content in the lambda temporary folder /tmp. And I want to get this json file to send it in my_bucket/folder/file.json
I saw many examples to create a file in s3 but Body parameter is in bytes and not a file.
This is my code (python3.7)
def alpaca_consent_customer_dev(event, context): # handler
# TODO implement
request_id = context.aws_request_id
print('START - RequestID: {}'.format(request_id))
# function to write json file
def write_json(target_path, target_file, data):
if not os.path.exists(target_path):
try:
os.makedirs(target_path)
except Exception as e:
print(e)
raise
with open(os.path.join(target_path, target_file), 'w') as f:
json.dump(data, f)
try:
s3 = boto3.client('s3', region_name="us-west-2")
request_id = context.aws_request_id
print('START - RequestID: {}'.format(request_id))
# Get message from SQS queue
for record in event['Records']:
data = record
# Get message from SQS
data_loaded = json.loads(data['body'])
sns_message_id = data_loaded['MessageId']
print('data loaded type:', type(data_loaded))
data_saved = json.dumps(data_loaded)
# Create json file in temporary folder
write_json('/tmp', sns_message_id+'.json', data_saved)
# Check if file exists
print(glob.glob("/tmp/*.json"))
# result: ['/tmp/3bb1c0bc-68d5-5c4d-b827-021301.json']
s3.put_object(Body='/tmp/'+sns_message_id + '.json', Bucket='mybucket', Key='my_sub_bucket/' + datetime.datetime.today().strftime('%Y%m%d')+ '/'+ sns_message_id + '.json')
except Exception as e:
raise Exception('ERROR lambda failed: {}'.format(str(e)))
Thanks for your help. Regards.

There's an official example in the boto3 docs:
import logging
import boto3
from botocore.exceptions import ClientError
def upload_file(file_name, bucket, object_name=None):
"""Upload a file to an S3 bucket
:param file_name: File to upload
:param bucket: Bucket to upload to
:param object_name: S3 object name. If not specified then file_name is used
:return: True if file was uploaded, else False
"""
# If S3 object_name was not specified, use file_name
if object_name is None:
object_name = file_name
# Upload the file
s3_client = boto3.client('s3')
try:
response = s3_client.upload_file(file_name, bucket, object_name)
except ClientError as e:
logging.error(e)
return False
return True
You can just use the upload_file method of the s3 client.

Related

How can I scrape PDFs within a Lambda function and save them to an S3 bucket?

I'm trying to develop a simple lambda function that will scrape a pdf and save it to an s3 bucket given the url and the desired filename as input data. I keep receiving the error "Read-only file system,' and I'm not sure if I have to change the bucket permissions or if there is something else I am missing. I am new to S3 and Lambda and would appreciate any help.
This is my code:
import urllib.request
import json
import boto3
def lambda_handler(event, context):
s3 = boto3.client('s3')
url = event['url']
filename = event['filename'] + ".pdf"
response = urllib.request.urlopen(url)
file = open(filename, 'w')
file.write(response.read())
s3.upload_fileobj(response.read(), 'sasbreports', filename)
file.close()
This was my event file:
{
"url": "https://purpose-cms-preprod01.s3.amazonaws.com/wp-content/uploads/2022/03/09205150/FY21-NIKE-Impact-Report_SASB-Summary.pdf",
"filename": "nike"
}
When I tested the function, I received this error:
{
"errorMessage": "[Errno 30] Read-only file system: 'nike.pdf.pdf'",
"errorType": "OSError",
"requestId": "de0b23d3-1e62-482c-bdf8-e27e82251941",
"stackTrace": [
" File \"/var/task/lambda_function.py\", line 15, in lambda_handler\n file = open(filename + \".pdf\", 'w')\n"
]
}
AWS Lambda has limited space in /tmp, the sole writable location.
Writing into this space can be dangerous without a proper disk management since this storage is kept alive across multiple executions. It can lead to a saturation or unexpected file share with previous requests.
Instead of saving locally the PDF, write it directly to S3, without involving file system this way:
import urllib.request
import json
import boto3
def lambda_handler(event, context):
s3 = boto3.client('s3')
url = event['url']
filename = event['filename']
response = urllib.request.urlopen(url)
s3.upload_fileobj(response.read(), 'sasbreports', filename)
BTW: The .pdf appending should be removed according your use case.
AWS Lambda functions can only write to the /tmp/ directory. All other directories are Read-Only.
Also, there is a default limit of 512MB for storage in /tmp/, so make sure you delete the files after upload it to S3 for situations where the Lambda environment is re-used for future executions.

Lambda doesn't copy object to another bucket when delete object event triggers

Lambda below works just fine with the object create event triggers but doesn't copy object on delete object event. Turning versioning on (to switch to deletes with markers instead of permanent) doesn't change it. Lambda role has arn:aws:iam::aws:policy/AmazonS3FullAccess and arn:aws:iam::aws:policy/AWSLambda_FullAccess policies attached.
What is a problem with this function?
import json
import boto3
# boto3 S3 initialization
s3_client = boto3.client("s3")
def lambda_handler(event, context):
source_bucket_name = event['Records'][0]['s3']['bucket']['name']
destination_bucket_name = source_bucket_name + '-glacier'
print(f'Copying from {source_bucket_name} to {destination_bucket_name}')
print("Event :", event)
# Filename of an object (with path)
file_key_name = event['Records'][0]['s3']['object']['key']
# Copy Source Object
copy_source_object = {'Bucket': source_bucket_name, 'Key': file_key_name}
# S3 copy object operation
s3_client.copy_object(CopySource=copy_source_object, Bucket=destination_bucket_name, Key=file_key_name, StorageClass='GLACIER')
return {
'statusCode': 200,
'body': json.dumps('Hello from S3 events Lambda!')
}

Kaggle login and unzip file to store in s3 bucket

Create a lambda function for python 3.7.
Role attached to the lambda function should have S3 access and lambda basic execution.
Read data from https://www.kaggle.com/therohk/india-headlines-news-dataset/download and save into S3 as CSV. file is zip how to unzip and store in temp file
Getting Failed in AWS Lambda function:
Lambda Handler to download news headline dataset from kaggle
import urllib3
import boto3
from botocore.client import Config
http = urllib3.PoolManager()
def lambda_handler(event, context):
bucket_name = 'news-data-kaggle'
file_name = "india-news-headlines.csv"
lambda_path = "/tmp/" +file_name
kaggle_info = {'UserName': "bossdk", 'Password': "xxx"}
url = "https://www.kaggle.com/account/login"
data_url = "https://www.kaggle.com/therohk/india-headlines-news-dataset/download"
r = http.request('POST',url,kaggle_info)
r = http.request('GET',data_url)
f = open(lambda_path, 'wb')
for chunk in r.iter_content(chunk_size = 512 * 1024):
if chunk:
f.write(chunk)
f.close()
data = ZipFile(lambda_path)
# S3 Connect
s3 = boto3.resource('s3',config=Config(signature_version='s3v4'))
# Uploaded File
s3.Bucket(bucket_name).put(Key=lambda_path, Body=data, ACL='public-read')
return {
'status': 'True',
'statusCode': 200,
'body': 'Dataset Uploaded'
}

Unable to Create S3 Bucket(in specific Region) using AWS Python Boto3

I am trying to create bucket using aws python boto 3.
Here is my code:-
import boto3
response = S3_CLIENT.create_bucket(
Bucket='symbols3arg',
CreateBucketConfiguration={'LocationConstraint': 'eu-west-1'}
)
print(response)
I am getting below error:-
botocore.exceptions.ClientError: An error occurred (IllegalLocationConstraintException) when calling the CreateBucket operation: The unspecified location constraint is incompatible for the region specific endpoint this request was sent to.
This happens you configured a different region during aws configure in specifying a different region in s3 client object initiation.
Suppose my AWS config look like
$ aws configure
AWS Access Key ID [None]: AKIAIOSFODEXAMPLE
AWS Secret Access Key [None]: wJalrXUtnFEMI/K7MDENG/bPxRfiCYEXAMPLEKEY
Default region name [None]: us-west-2
Default output format [None]: json
and my python script for creating bucket
import logging
import boto3
from botocore.exceptions import ClientError
def create_bucket(bucket_name, region=None):
# Create bucket
try:
if region is None:
s3_client = boto3.client('s3')
s3_client.create_bucket(Bucket=bucket_name)
else:
s3_client = boto3.client('s3')
location = {'LocationConstraint': region}
s3_client.create_bucket(Bucket=bucket_name,
CreateBucketConfiguration=location)
except ClientError as e:
logging.error(e)
return False
return True
create_bucket("test-bucket-in-region","us-west-1")
This will throw the below error
ERROR:root:An error occurred (IllegalLocationConstraintException) when calling the CreateBucket operation: The us-west-1 location constraint is incompatible for the region specific endpoint this request was sent to.
To solve this issue all you need to specify the region in s3 client object initiation. A working example in different region regardless of aws configure
import logging
import boto3
from botocore.exceptions import ClientError
def create_bucket(bucket_name, region=None):
"""Create an S3 bucket in a specified region
If a region is not specified, the bucket is created in the S3 default
region (us-east-1).
:param bucket_name: Bucket to create
:param region: String region to create bucket in, e.g., 'us-west-2'
:return: True if bucket created, else False
"""
# Create bucket
try:
if region is None:
s3_client = boto3.client('s3')
s3_client.create_bucket(Bucket=bucket_name)
else:
s3_client = boto3.client('s3', region_name=region)
location = {'LocationConstraint': region}
s3_client.create_bucket(Bucket=bucket_name,
CreateBucketConfiguration=location)
except ClientError as e:
logging.error(e)
return False
return True
create_bucket("my-working-bucket","us-west-1")
create-an-amazon-s3-bucket
Send the command to S3 in the same region:
import boto3
s3_client = boto3.client('s3', region_name='eu-west-1')
response = s3_client.create_bucket(
Bucket='symbols3arg',
CreateBucketConfiguration={'LocationConstraint': 'eu-west-1'}
)
You can try the following code.
import boto3
client = boto3.client('s3',region_name="aws_region_code")
response = client.create_bucket(
Bucket='string'
)
Hope, it might helps.

pytest mocking with boto3

just learning python mocking in general and struggling with using Magicmock and pytest with boto3.
Here is my code block
def upload_to_s3(self, local_file, bucket, dest_file):
self.local_file = local_file
self.bucket = bucket
self.dest_file = dest_file
s3_client = self.prime_s3_client() # this method returns the boto3 client
try:
s3_client.upload_file(local_file, bucket, dest_file)
LOG_IT.info('File uploaded to S3 from: %s to %s.', local_file, dest_file)
except Exception:
LOG_IT.critical('The %s failed to upload to S3.', local_file)
This is the test that's not working:
def test_upload_to_s3(self, monkeypatch, aws):
mock_s3_client = MagicMock()
monkeypatch.setattr(boto3, 'client', mock_s3_client)
mock_upload_file = MagicMock()
monkeypatch.setattr(mock_s3_client, 'upload_file', mock_upload_file)
push_to_s3 = aws.upload_to_s3('localfile', 'chumbucket', 'destfile')
mock_upload_file.assert_called()
The error returned:
E AssertionError: Expected 'upload_file' to have been called.
Thank you!

Resources