'Python Lambda function using boto3 uploads 0 bytes image file to s3

My use case is that I'm trying to take a screenshot of a view in Tableau, and save that screenshot in a bucket in s3. This is done through a Lambda function written in Python. The Lambda is assigned full access rights to s3 and is connected to the Internet.

Everything essentially works - there's no issues with access rights to s3, a connection to the Tableau account can be established, and a file is uploaded to s3. There's no errors thrown when the code is tested. There's one issue though: the saved file is an empty 0 bytes file.

Here's the code:

import logging
import traceback
import os
import requests
from datetime import datetime, timezone
import pytz
import json
from dotenv import load_dotenv
import tableauserverclient as TSC
from slack.web.client import WebClient
from slack.errors import SlackApiError
import boto3
import nest_asyncio
nest_asyncio.apply()

def lambda_handler(event,context):

    def Tableau2Slack():
        
        try:
            #Tableau environemnt variables
            tabAccount=os.environ['tabAccount'],
            tabPass=os.environ['tabPass'],
            tabDomain=os.environ['tabDomain'],
            tabServer=os.environ['tabServer'],
            tabView1=os.environ['tabView1'],
            tabPath1=os.environ['tabPath1']

            s3 = boto3.client('s3')
            bucket=os.environ['bucket']
            
        
            #Let's connect to Tableau
            print("Talking to Tableau...\n")
            tableau_auth = TSC.TableauAuth(tabAccount, tabPass, tabDomain)
            server = TSC.Server(tabServer)

            # Searching Tableau Online account for View1
            with server.auth.sign_in(tableau_auth):
                server.use_server_version()
                req_option = TSC.RequestOptions()
                req_option.filter.add(TSC.Filter(TSC.RequestOptions.Field.Name,
                                                 TSC.RequestOptions.Operator.Equals, tabView1))
                all_views, pagination_item = server.views.get(req_option)

                # Error catching for bad View names
                if not all_views:
                    raise LookupError("View with the specified name was not found.")
                view_item = all_views[0]

                image_req_option = TSC.ImageRequestOptions(imageresolution=TSC.ImageRequestOptions.Resolution.High,maxage=1)
                server.views.populate_image(view_item, image_req_option)
                print("Image saved in temporary folder...\n")

                date = datetime.utcnow().strftime('%Y_%m_%d')

                # Save bytes as image
                with open('/tmp' + tabPath1, "wb") as image_file1:
                    s3.upload_file('/tmp' + tabPath1, bucket, date + '_' + tabPath1)
                print("Tableau image successfully saved to s3 as {0}".format(tabPath1), '\n')

        # Tableau try statement error handling
        except:
            traceback.print_exc()

    Tableau2Slack()
    
    return print('Success!')

I suspect that there's something wrong where the file is opened and then uploaded to s3, but can't figure out what.

Running the same code locally, but instead of...

                with open('/tmp/' + tabPath1, "wb") as image_file1:
                    s3.upload_file('/tmp/' + tabPath1, bucket, date + '_' + tabPath1)

...replacing it with...

            with open(tabPath1, "wb") as image_file1:
                image_file1.write(view_item.image)

...saves a proper file of about 250 kb.

Any idea what could be going on? I'm out of ideas...



Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source