Photo from my pro football officiating days. I had to leave the game at age 5 due to penalty flag-induced carpal tunnel. |
Summary
I'm going to cover personal use-case in this post around making a large number of family photos securely accessible to family members. I've been maintaining a website for years for this purpose. I decided recently that maintaining that site was more work than was really necessary. Google recently terminated their Photo application, but Drive works just fine for sharing photos. I had a large enough collection of photos to upload to Drive that it made sense to go to code to do it.
Architecture
Drive has a documented Python API. I set up a Google Cloud project with a Service Account that allows access to Drive. I used that Service Account for all my API calls to Drive.
The diagram below depicts the transfer scenario. The local images are stored in a year, year-month hierarchy. That hierarchy is replicated on Drive.
Code
Main Loop
I have my photos stored locally in a folder hierarchy that follows this: Year -> Year-Month. The loop below iterates through all the local year and year-month folders to upload files to Drive.
def upload_all_images(self): years = os.listdir(LOCAL_ROOT) for year in years: print('Uploading year: ' + year) year_path = os.path.join(LOCAL_ROOT, year) year_months = os.listdir(year_path) for year_month in year_months: print('Uploading year_month: ' + year_month) self.upload_folder_images(year, year_month)
Photo Folder Upload
The function below creates the necessary year and year-month folders on Drive if they don't already exist. It then iterates through the local year-month folder to upload each image file to Drive.
def upload_folder_images(self, year, year_month): year_month_path = os.path.join(os.path.join(LOCAL_ROOT, year), year_month) if (os.path.isdir(year_month_path)): year_folder_id = self.get_folder_id(year) if (not year_folder_id): year_folder_id = self.create_folder(year, self.root_folder_id) year_month_folder_id = self.get_folder_id(year_month) if (not year_month_folder_id): year_month_folder_id = self.create_folder(year_month, year_folder_id) for file in os.listdir(year_month_path): local_file_path = os.path.join(year_month_path,file) if (os.path.isfile(local_file_path)): try: self.upload_file(local_file_path, year_month_folder_id) except Exception as e: print(e)
File Upload
The code below checks to see if the file already exists on Drive. If not, then it calls necessary Drive API functions to upload the image.def upload_file(self, local_file_path, folder_id): file_name = os.path.basename(local_file_path) #check if file already exists on gdrive. if not, create the file on google drive. results = self.service.files().list(q="'" + folder_id + "' in parents and name = '" + file_name + "'", spaces='drive', fields='files(id)').execute(num_retries=NUM_TRIES) items = results.get('files', []) if not items: print('Uploading: ' + file_name) try: outfile = self.resize(local_file_path) media = MediaFileUpload(outfile) file_metadata = {'name': file_name, 'parents': [folder_id]} self.service.files().create(body=file_metadata, media_body=media, fields='id').execute(num_retries=NUM_TRIES) os.remove(outfile) except Exception as e: print(e) else: print('File already exists on gdrive: ' + file_name) return
Image Resizing
I use the PIL library to reduce the resolution (and thus size) of each image file to reduce my Drive space.
def resize(self, infile): outfile = os.path.join('./', os.path.basename(infile)) im = Image.open(infile) if max(im.size) < 1000: size = im.size else: size = (1000,1000) im.thumbnail(size, Image.ANTIALIAS) im.save(outfile, optimize=True, quality=85) return outfile
Source
Copyright ©1993-2024 Joey E Whelan, All rights reserved.