Upload files to Google Drive with Lambda (Python)

Upload files to Google Drive with Lambda (Python)

This article is a hands-on article for reviewing and fixing the knowledge gained by developing Serverless Web App Mosaic. It is one of w2or3w / items / 87b57dfdbcf218de91e2).

It would be nice to read this article after looking at the following.

introduction

S3 is good if you just save a file, but it's not suitable for people to refer to that file. Even if it is an image, it cannot be viewed as it is on a Web browser, and it is necessary to download it once and then view it. It's a bit of a hassle. In that respect, Google Drive is nice because you can browse images nicely with a browser or application, whether it is a PC or a smartphone.

content

Creating a Google Developers Console project

Go to Google's Developers Console. https://console.developers.google.com

If you do not have a project, create one. Screenshot 2020-01-06 at 22.43.06.png

Enable Google Drive API

Click "+ Enable APIs and Services" at the top of the "APIs and Services" screen on the console to enable Google Drive. Screenshot 2020-01-06 at 22.45.12.png Screenshot 2020-01-06 at 22.47.53.png Screenshot 2020-01-06 at 22.48.10.png

Creating credentials

After enabling the Google Drive API, create your credentials from "Credentials" in the left pane of the "APIs and Services" screen on the console. Select "Service Account" from the drop-down menu that appears when you press "+ Create Credentials" at the top of the Credentials page. Screenshot 2020-01-06 at 22.56.00.png

Please enter the service account name to create it. The service account ID under the service account name is important information, so please manage it so that it will not be leaked. ink (1).png

Service account confirmation

After creating the service account, browse the details of the created service account from "Service Account" in the left pane of the "IAM and Administration" screen of the console. ink (2).png

Creating a service account private key

From the service account details, click the "Edit" and "Create + Key" buttons to create the private key in JSON format and download the JSON file. ink (4).png

Create a folder in Google Drive and share it with your service account

Create a folder for uploading files in Google Drive. Here, the folder name is sample-drive. Then share that folder with the service account (email address) you created earlier. Screenshot 2020-01-06 at 23.11.04.png ink (3).png

The string after the URL of this folder will be used later in the program. (Hidden part of the capture below) ink (5).png

Well, this completes the settings on the Google side. Next, let's upload the file programmatically using the Google API.

Request Google API from Lambda (Python) with service account

First, rename the private key JSON file of the service account you downloaded earlier to an appropriate name and place it in the same location as lambda_function.py. I named it "service-account-key.json" here.

Then install the required libraries.

$ pip install google-api-python-client -t .
$ pip install oauth2client -t .

Now import what you need.

lambda_function.py


  :
from googleapiclient.discovery import build 
from googleapiclient.http import MediaFileUpload 
from oauth2client.service_account import ServiceAccountCredentials 
  :

Then upload the file with the following implementation.

lambda_function.py


  :
def uploadFileToGoogleDrive(fileName, localFilePath):
    try:
        ext = os.path.splitext(localFilePath.lower())[1][1:]
        if ext == "jpg":
            ext = "jpeg"
        mimeType = "image/" + ext

        service = getGoogleService()
        file_metadata = {"name": fileName, "mimeType": mimeType, "parents": ["*********************************"] } 
        media = MediaFileUpload(localFilePath, mimetype=mimeType, resumable=True) 
        file = service.files().create(body=file_metadata, media_body=media, fields='id').execute()

    except Exception as e:
        logger.exception(e)

def getGoogleService():
    scope = ['https://www.googleapis.com/auth/drive.file'] 
    keyFile = 'service-account-key.json'
    credentials = ServiceAccountCredentials.from_json_keyfile_name(keyFile, scopes=scope)
    
    return build("drive", "v3", credentials=credentials, cache_discovery=False) 

"parents": ["*********************************"] This part is a folder created in Google Drive Replace it with the string after the URL in.

Finally, just in case, I will post the entire program.

lambda_function.py


# coding: UTF-8
import boto3
import os
import json
from urllib.parse import unquote_plus
import numpy as np
import cv2
import logging
logger = logging.getLogger()
logger.setLevel(logging.INFO)
s3 = boto3.client("s3")
rekognition = boto3.client('rekognition')

from gql import gql, Client
from gql.transport.requests import RequestsHTTPTransport
ENDPOINT = "https://**************************.appsync-api.ap-northeast-1.amazonaws.com/graphql"
API_KEY = "da2-**************************"
_headers = {
    "Content-Type": "application/graphql",
    "x-api-key": API_KEY,
}
_transport = RequestsHTTPTransport(
    headers = _headers,
    url = ENDPOINT,
    use_json = True,
)
_client = Client(
    transport = _transport,
    fetch_schema_from_transport = True,
)

from googleapiclient.discovery import build 
from googleapiclient.http import MediaFileUpload 
from oauth2client.service_account import ServiceAccountCredentials 

def lambda_handler(event, context):
    bucket = event["Records"][0]["s3"]["bucket"]["name"]
    key = unquote_plus(event["Records"][0]["s3"]["object"]["key"], encoding="utf-8")
    logger.info("Function Start (deploy from S3) : Bucket={0}, Key={1}" .format(bucket, key))

    fileName = os.path.basename(key)
    dirPath = os.path.dirname(key)
    dirName = os.path.basename(dirPath)
    
    orgFilePath = "/tmp/" + fileName
    
    if (not key.startswith("public") or key.startswith("public/processed/")):
        logger.info("don't process.")
        return
    
    apiCreateTable(dirName, key)

    keyOut = key.replace("public", "public/processed", 1)
    dirPathOut = os.path.dirname(keyOut)

    try:
        s3.download_file(Bucket=bucket, Key=key, Filename=orgFilePath)

        orgImage = cv2.imread(orgFilePath)
        grayImage = cv2.cvtColor(orgImage, cv2.COLOR_RGB2GRAY)
        processedFileName = "gray-" + fileName
        processedFilePath = "/tmp/" + processedFileName
        uploadImage(grayImage, processedFilePath, bucket, os.path.join(dirPathOut, processedFileName), dirName, False)

        uploadFileToGoogleDrive(key, orgFilePath)
        detectFaces(bucket, key, fileName, orgImage, dirName, dirPathOut)

    except Exception as e:
        logger.exception(e)
        raise e
        
    finally:
        if os.path.exists(orgFilePath):
            os.remove(orgFilePath)

def uploadImage(image, localFilePath, bucket, s3Key, group, isUploadGoogleDrive):
    logger.info("start uploadImage({0}, {1}, {2}, {3})".format(localFilePath, bucket, s3Key, group))
    try:
        cv2.imwrite(localFilePath, image)
        s3.upload_file(Filename=localFilePath, Bucket=bucket, Key=s3Key)
        apiCreateTable(group, s3Key)
        if isUploadGoogleDrive:
            uploadFileToGoogleDrive(s3Key, localFilePath)
    except Exception as e:
        logger.exception(e)
        raise e
    finally:
        if os.path.exists(localFilePath):
            os.remove(localFilePath)

def apiCreateTable(group, path):
    logger.info("start apiCreateTable({0}, {1})".format(group, path))
    try:
        query = gql("""
            mutation create {{
                createSampleAppsyncTable(input:{{
                group: \"{0}\"
                path: \"{1}\"
              }}){{
                group path
              }}
            }}
            """.format(group, path))
        _client.execute(query)
    except Exception as e:
        logger.exception(e)
        raise e

def detectFaces(bucket, key, fileName, image, group, dirPathOut):
    logger.info("start detectFaces ({0}, {1}, {2}, {3}, {4})".format(bucket, key, fileName, group, dirPathOut))
    try:
        response = rekognition.detect_faces(
            Image={
                "S3Object": {
                    "Bucket": bucket,
                    "Name": key,
                }
            },
            Attributes=[
                "ALL",
            ]
        )

        name, ext = os.path.splitext(fileName)
        
        jsonFileName = name + ".json"
        localPathJSON = "/tmp/" + jsonFileName
        with open(localPathJSON, 'w') as f:
            json.dump(response, f, ensure_ascii=False)
        s3.upload_file(Filename=localPathJSON, Bucket=bucket, Key=os.path.join(dirPathOut, jsonFileName))
        if os.path.exists(localPathJSON):
            os.remove(localPathJSON)
        
        imgHeight = image.shape[0]
        imgWidth = image.shape[1]
        index = 0
        for faceDetail in response["FaceDetails"]:
            index += 1
            faceFileName = "face_{0:03d}".format(index) + ext
            box = faceDetail["BoundingBox"]
            x = max(int(imgWidth * box["Left"]), 0)
            y = max(int(imgHeight * box["Top"]), 0)
            w = int(imgWidth * box["Width"])
            h = int(imgHeight * box["Height"])
            logger.info("BoundingBox({0},{1},{2},{3})".format(x, y, w, h))
            
            faceImage = image[y:min(y+h, imgHeight-1), x:min(x+w, imgWidth)]
            
            localFaceFilePath = os.path.join("/tmp/", faceFileName)
            uploadImage(faceImage, localFaceFilePath, bucket, os.path.join(dirPathOut, faceFileName), group, False)
            cv2.rectangle(image, (x, y), (x+w, y+h), (0, 0, 255), 3)

        processedFileName = "faces-" + fileName
        processedFilePath = "/tmp/" + processedFileName
        uploadImage(image, processedFilePath, bucket, os.path.join(dirPathOut, processedFileName), group, True)
    except Exception as e:
        logger.exception(e)
        raise e


def uploadFileToGoogleDrive(fileName, localFilePath):
    try:
        ext = os.path.splitext(localFilePath.lower())[1][1:]
        if ext == "jpg":
            ext = "jpeg"
        mimeType = "image/" + ext

        service = getGoogleService()
        file_metadata = {"name": fileName, "mimeType": mimeType, "parents": ["*********************************"] } 
        media = MediaFileUpload(localFilePath, mimetype=mimeType, resumable=True) 
        file = service.files().create(body=file_metadata, media_body=media, fields='id').execute()

    except Exception as e:
        logger.exception(e)

def getGoogleService():
    scope = ['https://www.googleapis.com/auth/drive.file'] 
    keyFile = 'service-account-key.json'
    credentials = ServiceAccountCredentials.from_json_keyfile_name(keyFile, scopes=scope)
    
    return build("drive", "v3", credentials=credentials, cache_discovery=False) 

Operation check

When you upload an image from a web app, Lambda's Python runs to process the image, and then you also upload the original image and the image with ROI marked on the face to Google Drive. Unlike S3, I'm happy to see it as a picture. Screenshot 2020-01-08 at 22.51.30.png

Afterword

I would like to touch Google's cloud service as well. I like Google. This is one of the goals for 2020.

By the way, there are many Google products around me such as Pixel 3a, Chromebook, Google Home Mini, Chromecast, Google Wifi. As for the service, I use Google Fit with Mi Band on a daily basis as well as Gmail, Calendar, Drive, Photo, etc., and I enjoy posting Google Map as a local guide. Google One has also been upgraded to 2TB (¥ 13,000 per year).

OK Google, I love you!

Recommended Posts

Upload files to Google Drive with Lambda (Python)
Upload images to Google Drive with Python
Regularly upload files to Google Drive using the Google Drive API in Python
Access Google Drive with Python
How to load files in Google Drive with Google Colaboratory
Upload to a shared drive with Google Drive API V3
Download Google Drive files in Python
How to upload files to Cloud Storage with Firebase's python SDK
Upload and delete files to Google Cloud Storages with django-storage
How to search Google Drive with Google Colaboratory
Connect to s3 with AWS Lambda Python
Convert HEIC files to PNG files with Python
How to upload with Heroku, Flask, Python, Git (4)
Upload files with Django
Drive WebDriver with python
Sample to send slack notification with python lambda
Download files directly to Google Drive (using Google Colaboratory)
Export RDS snapshot to S3 with Lambda (Python)
Upload what you got in request to S3 with AWS Lambda Python
How to upload with Heroku, Flask, Python, Git (Part 3)
Easy way to scrape with python using Google Colab
Write multiple records to DynamoDB with Lambda (Python, JavaScript)
How to upload with Heroku, Flask, Python, Git (Part 1)
How to upload with Heroku, Flask, Python, Git (Part 2)
Connect to BigQuery with Python
Operate TwitterBot with Lambda, Python
Connect to Wikipedia with Python
Post to slack with Python 3
Study Python with Google Colaboratory
Sorting image files with Python (2)
Sort huge files with python
Sorting image files with Python (3)
Sorting image files with Python
Integrate PDF files with Python
Reading .txt files with Python
Switch python to 2.7 with alternatives
Write to csv with Python
Mount google drive with google-drive-ocamlfuse
Google Drive Api Tips (Python)
How to use Python lambda
How to extract any appointment in Google Calendar with Python
[AWS] Try adding Python library to Layer with SAM + Lambda (Python)
Challenge problem 5 with Python: lambda ... I decided to copy without
Post a message to Google Hangouts Chat with a thread (Python)
Save images on the web to Drive with Python (Colab)
[Python] Regularly export from CloudWatch Logs to S3 with Lambda
Operate limited shared Google Calendar with Lambda (Python) [cloudpack Osaka]
Python: Extract file information from shared drive with Google Drive API
Until we created a mechanism to upload files shared in Slack to Google Drive without a server
[Introduction to Udemy Python 3 + Application] 58. Lambda
Let's upload S3 files with CLI
Python: How to use async with
Link to get started with python
[Python] Write to csv file with Python
Manipulating EAGLE .brd files with Python
Create folders from '01' to '12' with python
Nice to meet you with python
Try to operate Facebook with Python
Face detection with Lambda (Python) + Rekognition
Output to csv file with Python
[Lambda] [Python] Post to Twitter from Lambda!