![]() |
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.