Want to directly upload files from an ancient raspberry pi with a lite os or any device that has no GUI but has trust issues with third-party tools..?
Pre-requisites
- Install the Google Libraries:
1
2
$ pip install — upgrade google-api-python-client
$ pip install — upgrade google-auth-oauthlib google-auth-httplib2
- Create or select a project in the Google API Console. Complete the following tasks in the API Console for your project:
- In the library panel, search for the Drive API and make sure the API is enabled for your project if not done already.
- In the credentials panel, create an OAuth 2.0 client ID. Set the application type to TV and limited input devices. You need to use OAuth 2.0 credentials for requests that require user authorization in this case, this is required.
- Download the JSON file that contains your OAuth 2.0 credentials. The file has a name like client_secret_CLIENTID.json, where CLIENTID is the client ID for your project.
the session will be generated for offline access however I am not sure if Google will allow permanent logins with unverified apps so this may demand amendments for token refreshes but that’s out of scope.
- Move the JSON to a location and note down the path and update the
CLIENT_SECRETS_FILE
on the script. - At first, you will have to generate a token from a machine that has GUI. This is a one-time process:
- Run the script on a machine with GUI, visit the Google URL provided by the script to authorize the application, which if done correctly will give you a
token.json
. Use thetoken.json
file to do the authentication on the devices with no GUI.
- Run the script on a machine with GUI, visit the Google URL provided by the script to authorize the application, which if done correctly will give you a
- Yes, as you guessed already, move the
token.json
file to the device with no GUI. (hint:SCP transfer
) - Update
TOKEN_FILE
on the script with the path to thetoken.json
- Update
PARENT_DRIVE_FOLDER
on the script with the id of the folder to which the files will be uploaded. The id can be fetched using the drive API or it (as highlighted in the image) can be easily found from the URL. To navigate to the web ui, login to Google Drive and then navigate to an existing folder or you could create a folder that you would like to be the parent folder for all the files and folders uploaded using the script. Then copy the parent id from the address bar.
Code in Detail
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
#!/usr/bin/python3
import argparse
import os
import re
import google.oauth2.credentials
import google_auth_oauthlib.flow
from googleapiclient.discovery import build
from googleapiclient.errors import HttpError
from google_auth_oauthlib.flow import InstalledAppFlow
from google.oauth2.credentials import Credentials
from apiclient.http import MediaFileUpload
import mimetypes
CLIENT_SECRETS_FILE = 'client_secret.json'
TOKEN_FILE = 'token.json'
PARENT_DRIVE_FOLDER = '<id_of_the_google_drive_folder>'
# This OAuth 2.0 access scope allows for read-only access to the authenticated
# user's account, but not other types of account access.
SCOPES = ['https://www.googleapis.com/auth/drive']
API_SERVICE_NAME = 'drive'
API_VERSION = 'v3'
def get_authenticated_service_drive():
creds = None
# The file token.json stores the user's access and refresh tokens, and is
# created automatically when the authorization flow completes for the first
# time.
if os.path.exists(TOKEN_FILE):
creds = Credentials.from_authorized_user_file(TOKEN_FILE, SCOPES)
return build(API_SERVICE_NAME, API_VERSION, credentials = creds)
# If there are no (valid) credentials available, let the user log in.
if not creds or not creds.valid:
if creds and creds.expired and creds.refresh_token:
creds.refresh(Request())
else:
flow = InstalledAppFlow.from_client_secrets_file(
CLIENT_SECRETS_FILE, SCOPES)
creds = flow.run_console()
# Save the credentials for the next run
with open(TOKEN_FILE, 'w') as token:
token.write(creds.to_json())
return build(API_SERVICE_NAME, API_VERSION, credentials = creds)
def drive_upload(service, filename):
if os.path.isdir(filename):
for subdir, dirs, files in os.walk(filename):
for file in files:
head, filename_forDrive = os.path.split(file)
mimeTypeOfFile = mimetypes.guess_type(f"{file}")[0]
exists, file_id= search_if_file_exists(service, filename_forDrive, mimeTypeOfFile)
try:
file_metadata = {'name': filename_forDrive,'mimeType': mimetypes.guess_type(f"{file}"),'parents':[PARENT_DRIVE_FOLDER]}
media = MediaFileUpload(str(os.path.join(subdir, file)), chunksize=50*1024*1024, resumable=True)
if exists:
file_metadata = {'name': filename_forDrive,'mimeType': mimetypes.guess_type(f"{file}")}
request = service.files().update(fileId=file_id, body=file_metadata,media_body=media,fields='id')
else:
request = service.files().create(body=file_metadata,media_body=media,fields='id')
response = None
while response is None:
status, response = request.next_chunk()
if status:
print ("Uploaded %d%%." % int(status.progress() * 100))
file = request.execute()
print (f'Uploaded {filename_forDrive}')
except Exception as e:
print (str(e))
else:
head, filename_forDrive = os.path.split(filename)
files_list = [f'{filename}']
for file in files_list:
mimetypee = mimetypes.guess_type(f"{filename}"[0])
exists = search_if_file_exists(service, filename_forDrive, mimetypee)
try:
file_metadata = {'name': filename_forDrive,'mimeType': mimetypes.guess_type(f"{filename}"),'parents':[PARENT_DRIVE_FOLDER]}
media = MediaFileUpload(str(file), chunksize=50*1024*1024, resumable=True)
#request = service.files().create(body=file_metadata,media_body=media,fields='id')
if exists:
file_metadata = {'name': filename_forDrive,'mimeType': mimetypes.guess_type(f"{filename}")}
request = service.files().update(body=file_metadata,media_body=media,fields='id')
else:
request = service.files().create(body=file_metadata,media_body=media,fields='id')
response = None
while response is None:
status, response = request.next_chunk()
if status:
print ("Uploaded %d%%." % int(status.progress() * 100))
file = request.execute()
print (f'Uploaded {filename_forDrive}')
except Exception as e:
print (str(e))
def search_if_file_exists(service, filename_forDrive, mimeTypeOfFile):
page_token = None
while True:
if mimeTypeOfFile is None:
response = service.files().list(q=f"name='{filename_forDrive}' and parents in '{PARENT_DRIVE_FOLDER}' and trashed = false",
spaces='drive',
fields='nextPageToken, files(id, name, parents, mimeType)',
pageToken=page_token).execute()
else:
response = service.files().list(q=f"mimeType='{mimeTypeOfFile}' and name='{filename_forDrive}' and parents in '{PARENT_DRIVE_FOLDER}' and trashed = false",
spaces='drive',
fields='nextPageToken, files(id, name, parents, mimeType)',
pageToken=page_token).execute()
if response:
for file in response.get('files', []):
print ("File already exists")
print ('Found file: %s (%s)' % (file.get('name'), file.get('id')))
return True, file.get('id')
page_token = response.get('nextPageToken', None)
if page_token is None:
return False, 'Duh'
if __name__ == '__main__':
parser = argparse.ArgumentParser()
parser.add_argument('--file_path', help='File Path', required=True)
args = parser.parse_args()
if args.file_path:
drive_service = get_authenticated_service_drive()
drive_upload(drive_service, args.file_path)
else:
print("Invalid path, nothing to upload to drive.")
Run the Code
To upload the files in a folder and its subfolders:
1
$ python3 drive_upload.py –file_path /home/pi/drive_folder
To upload a file:
1
$ python3 drive_upload.py — file_path /home/pi/drive_file.zip
P.S.
If you are on Linux, just add an alias on .bashrc or .zshrc or whatever you use:
1
alias drive=”python3 /home/pi/drive/drive_upload.py — file_path”
…so that you can simply do the following to upload the files in the temp directory on the home directory, for example:
1
$ drive ~/temp/
Thank you!