• 跳至主要內容
  • 跳至主要資訊欄
  • 跳至頁尾
  • Python應用
    • Python基礎操作
    • Web
      • Django
      • Flask
    • Chat bot
      • Line Bot
      • Telegram Bot
    • GUI
      • QT5
    • Google API
      • Google Drive
      • Google Gmail
      • Google sheet
    • 網頁技術
      • WordPress
      • HTML
      • CSS
      • JavaScript
  • 基本測試技術
    • Linux
    • 功能測試
    • 使用者測試
    • 壓力測試
    • 封包測試
    • 測試報告撰寫
    • 編寫測試用例
  • 自動化測試技術
    • API 自動化測試
      • Python API 測試
      • Postman
      • Jmeter
    • UI 自動化測試
      • AirTest
      • Appium
      • Robot Framework
      • Selenium
  • 問題跟蹤系統
    • Jira
    • Redmine
  • CI/CD
    • Git
    • Jenkins
    • Docker
    • K8S
    • Ngrok
  • 資料庫
    • MongoDB
    • MySQL
    • PostgreSQL
    • Redis
  • 伺服器
    • Heroku
    • Linode
    • Cloudways
    • GCP
    • AWS
  • 關於我
測試先生

測試先生

提供測試相關領域的技術文章以及其他軟體技術相關文章,如WordPress,Js,Css,Html,Python,Docker,Kubernetes,devops,k8s,jenkins,git,heroku,line,telegram

目前位置: 首頁 / Python應用 / Google API / Google Drive / 【Google API 教學】Google Drive API upload 使用 Python 上傳到指定資料夾 基本教學

【Google API 教學】Google Drive API upload 使用 Python 上傳到指定資料夾 基本教學

文章更新日期: 2022 年 4 月 19 日

透過這篇文章【Google API 教學】Google Drive API upload 使用 Python 上傳單一檔案 基本教學 可以上傳檔案到根目錄底下

但是你想要放置到某個Google 雲端資料夾底下,就需要修改一下程式碼

那要如何改成可以上傳檔案到指定資料夾內呢?

其實不難只要給個路徑就行,那要怎麼寫才是重點對吧!

此文章也會分享程式碼提供給需要的朋友參考。

假設你還不知道怎麼申請Google API 憑證可參考這篇:【Google API 教學】如何申請 OAuth 2.0 憑證? 使用Google Drive API 做示範給你看!

內容目錄

  • 使用 Python 上傳檔案到 Google Drive 指定資料夾 完整程式碼
    • Example 1: 先建立一個資料夾名稱為TestAPI,檔案會上傳到這個資料夾內
    • Example 2: 建立一個TestAPI資料夾裡面再建立一個TestAPI資料夾,兩個資料夾都會有上傳的檔案 Error
    • Example 3: 建立一個TestAPI123資料夾,裡面又建立一個TestAPI資料夾,這時檔案只會上傳到TestAPI,不會上傳到TestAPI123
  • 上傳檔案到Google Drive 指定資料夾程式碼 說明
    • is_update_file_function
    • update_drive_service_folder_name
      • 資料夾名稱相同
      • 放置檔案到資料夾底下的資料夾
    • update_drive_service_name
    • update_file_path
  • 上傳檔案到Google Drive 指定資料夾程式碼 main 程式碼區塊 說明
  • 上傳檔案到Google Drive 指定資料夾程式碼 search_folder 程式碼區塊 說明
    • 篩選條件程式碼 介紹
  • 上傳檔案到Google Drive 指定資料夾程式碼 update_file 程式碼區塊 說明
  • 更多技術文章

使用 Python 上傳檔案到 Google Drive 指定資料夾 完整程式碼

可以直接將下方完整程式碼複製到你的本地端使用

下方會先介紹三種範例說明,主要都是設定上傳檔案名稱為aaa.txt,上傳到Google Drive 資料夾名稱設定為 TestAPI

from __future__ import print_function
import os
import io
import time
from googleapiclient.discovery import build
from googleapiclient.http import MediaFileUpload, MediaIoBaseDownload
from httplib2 import Http
from oauth2client import file, client, tools


# 權限必須
SCOPES = ['https://spreadsheets.google.com/feeds', 'https://www.googleapis.com/auth/drive']


def delete_drive_service_file(service, file_id):
    service.files().delete(fileId=file_id).execute()


def update_file(service, update_drive_service_name, local_file_path, update_drive_service_folder_id):
    """
    將本地端的檔案傳到雲端上
    :param update_drive_service_folder_id: 判斷是否有 Folder id 沒有的話,會上到雲端的目錄
    :param service: 認證用
    :param update_drive_service_name: 存到 雲端上的名稱
    :param local_file_path: 本地端的位置
    :param local_file_name: 本地端的檔案名稱
    """

    print("正在上傳檔案...")
    if update_drive_service_folder_id is None:
        file_metadata = {'name': update_drive_service_name}
    else:
        print("雲端資料夾id: %s" % update_drive_service_folder_id)
        file_metadata = {'name': update_drive_service_name,
                         'parents': update_drive_service_folder_id}

    media = MediaFileUpload(local_file_path, )
    file_metadata_size = media.size()
    start = time.time()
    file_id = service.files().create(body=file_metadata, media_body=media, fields='id').execute()
    end = time.time()

    print("上傳檔案成功!")
    print('雲端檔案名稱為: ' + str(file_metadata['name']))
    print('雲端檔案ID為: ' + str(file_id['id']))
    print('檔案大小為: ' + str(file_metadata_size) + ' byte')
    print("上傳時間為: " + str(end-start))

    return file_metadata['name'], file_id['id']


def search_folder(service, update_drive_folder_name=None):
    """
    如果雲端資料夾名稱相同,則只會選擇一個資料夾上傳,請勿取名相同名稱
    :param service: 認證用
    :param update_drive_folder_name: 取得指定資料夾的id,沒有的話回傳None,給錯也會回傳None
    """

    get_folder_id_list = []
    print(len(get_folder_id_list))
    if update_drive_folder_name is not None:
        response = service.files().list(fields="nextPageToken, files(id, name)", spaces='drive',
                                       q = "name = '" + update_drive_folder_name + "' and mimeType = 'application/vnd.google-apps.folder' and trashed = false").execute()

        for file in response.get('files', []):
            # Process change
            print('Found file: %s (%s)' % (file.get('name'), file.get('id')))
            get_folder_id_list.append(file.get('id'))

        if len(get_folder_id_list) == 0:
            print("你給的資料夾名稱沒有在你的雲端上!,因此檔案會上傳至雲端根目錄")
            return None
        else:
            return get_folder_id_list

    return None


def search_file(service, update_drive_service_name, is_delete_search_file=False):
    """
    本地端
    取得到雲端名稱,可透過下載時,取得file id 下載
    :param service: 認證用
    :param update_drive_service_name: 要上傳到雲端的名稱
    :param is_delete_search_file: 判斷是否需要刪除這個檔案名稱
    """

    # Call the Drive v3 API
    results = service.files().list(fields="nextPageToken, files(id, name)", spaces='drive',
                                   q="name = '" + update_drive_service_name + "' and trashed = false",
                                   ).execute()

    items = results.get('files', [])

    if not items:
        print('沒有發現你要找尋的 ' + update_drive_service_name + ' 檔案.')
    else:
        print('搜尋的檔案: ')

        for item in items:
            times = 1
            print(u'{0} ({1})'.format(item['name'], item['id']))
            if is_delete_search_file is True:
                print("刪除檔案為:" + u'{0} ({1})'.format(item['name'], item['id']))
                delete_drive_service_file(service, file_id=item['id'])
            if times == len(items):
                return item['id']
            else:
                times += 1


def trashed_file(service, is_delete_trashed_file=False):
    """
    抓取到雲端上垃圾桶內的全部檔案,進行刪除
    :param service: 認證用
    :param is_delete_trashed_file: 是否要刪除垃圾桶資料
    """
    results = service.files().list(fields="nextPageToken, files(id, name)", spaces='drive', q="trashed = true",
                                   ).execute()
    items = results.get('files', [])

    if not items:
        print('垃圾桶無任何資料.')
    else:
        print('垃圾桶檔案: ')

        for item in items:
            print(u'{0} ({1})'.format(item['name'], item['id']))
            if is_delete_trashed_file is True:
                print("刪除檔案為:" + u'{0} ({1})'.format(item['name'], item['id']))
                delete_drive_service_file(service, file_id=item['id'])


def main(is_update_file_function=False, update_drive_service_folder_name=None, update_drive_service_name=None, update_file_path=None):
    """
    :param update_drive_service_folder_name: 給要上傳檔案到雲端的資料夾名稱,預設則是上傳至雲端目錄
    :param is_update_file_function: 判斷是否執行上傳的功能
    :param update_drive_service_name: 要上傳到雲端上的檔案名稱
    :param update_file_path: 要上傳檔案的位置以及名稱
    """

    print("is_update_file_function: %s" % is_update_file_function)
    print("update_drive_service_folder_name: %s" % update_drive_service_folder_name)
    store = file.Storage('token.json')
    creds = store.get()

    if not creds or creds.invalid:
        flow = client.flow_from_clientsecrets('credentials.json', SCOPES)
        creds = tools.run_flow(flow, store)

    service = build('drive', 'v3', http=creds.authorize(Http()))
    print('*' * 10)

    if is_update_file_function is True:
        print(update_file_path + update_drive_service_name)
        print("=====執行上傳檔案=====")
        # 清空 雲端垃圾桶檔案
        # trashed_file(service=service, is_delete_trashed_file=True)
        get_folder_id = search_folder(service = service, update_drive_folder_name = update_drive_service_folder_name)

        # 搜尋要上傳的檔案名稱是否有在雲端上並且刪除
        search_file(service=service, update_drive_service_name=update_drive_service_name,
                    is_delete_search_file=True)

        # 檔案上傳到雲端上
        update_file(service=service, update_drive_service_name=update_drive_service_name,
                    local_file_path=os.getcwd() + '/' + update_drive_service_name, update_drive_service_folder_id=get_folder_id)
        print("=====上傳檔案完成=====")


if __name__ == '__main__':
    main(is_update_file_function=bool(True), update_drive_service_folder_name='TestAPI', update_drive_service_name='aaa.txt', update_file_path=os.getcwd() + '/')

Example 1: 先建立一個資料夾名稱為TestAPI,檔案會上傳到這個資料夾內

%E8%9E%A2%E5%B9%95%E5%BF%AB%E7%85%A7 2020 02 12 %E4%B8%8B%E5%8D%882.07.24
建立一個TestAPI資料夾,執行程式會將aaa.txt上傳到此資料夾底下

Example 2: 建立一個TestAPI資料夾裡面再建立一個TestAPI資料夾,兩個資料夾都會有上傳的檔案 Error

看似2020年9月30日起,本文章提供的程式碼沒辦法上傳到相同資料夾名稱,需要用別的方式,否則會出現下面問題,可以參考Google Drive API文檔

googleapiclient.errors.HttpError: 
<HttpError 403 when requesting https://www.googleapis.com/upload/drive/v3/files?fields=id&alt=json&uploadType=multipart returned "Increasing the number of parents is not allowed". Details: "[{'domain': 'global', 'reason': 'cannotAddParent', 'message': 'Increasing the number of parents is not allowed'}]">

%E8%9E%A2%E5%B9%95%E5%BF%AB%E7%85%A7 2020 02 12 %E4%B8%8B%E5%8D%882.07.30
建立TestAPI資料夾後,裡面又建立一個TestAPI資料夾,上傳aaa.txt的位置
所以執行後,會將檔案上傳至相同資料夾內 (此文章程式碼再2020年9月30日開始無法上傳至同一個資料夾名稱)

Example 3: 建立一個TestAPI123資料夾,裡面又建立一個TestAPI資料夾,這時檔案只會上傳到TestAPI,不會上傳到TestAPI123

%E8%9E%A2%E5%B9%95%E5%BF%AB%E7%85%A7 2020 02 12 %E4%B8%8B%E5%8D%882.17.02
沒有aaa.txt檔案,資料夾名稱不符合
%E8%9E%A2%E5%B9%95%E5%BF%AB%E7%85%A7 2020 02 12 %E4%B8%8B%E5%8D%882.12.35
因為上傳資料夾名稱符合,所以會有上傳的檔案

上傳檔案到Google Drive 指定資料夾程式碼 說明

執行程式碼時,我們需要有四個參數值,分別為

  • is_update_file_function
  • update_drive_service_folder_name
  • update_drive_service_name
  • update_file_path

is_update_file_function

是否要上傳檔案?預設為True

當初設計主要是針對Jenkins做判斷,上傳還是下載使用。

update_drive_service_folder_name

放置到Google Drive 某個資料夾內,預設為None(放置雲端目錄)

假設你放置到你雲端資料夾請給一個雲端上的資料夾名稱

例如 update_drive_service_folder_name = “TestAPI”,這時程式碼會將你給的檔案名稱丟入到TestAPI內

可能會有以下幾種可能性

資料夾名稱相同

資料夾有多個相同名稱的話,如”TestAPI”,則是都會丟到你的全部等於TestAPI的資料夾

放置檔案到資料夾底下的資料夾

資料夾下方又放一個資料夾,我只想丟到最後一個資料夾裡面又該怎麼做呢?

假設你想放在A資料夾裡面的B資料夾,你只需要給B資料夾名稱就可以!

例如:update_drive_service_folder_name = “B”。

因為在Google Drive上,他會針對每一個資料夾給一個ID。

如果你雲端資料夾名稱給錯的話,則是上傳到雲端目錄喔!此程式碼不會自動建立雲端資料夾。

update_drive_service_name

要上傳本地端檔案到雲端的名稱,需要包含副檔名

update_file_path

如果是放在同一個目錄要上傳,就不需要修改程式碼,如果不同需要修改

不然會找不到你要上傳的檔案。

if __name__ == '__main__':

    main(is_update_file_function=bool(True), update_drive_service_folder_name = None, update_drive_service_name='aaa.txt', update_file_path=os.getcwd() + '/')

上傳檔案到Google Drive 指定資料夾程式碼 main 程式碼區塊 說明

我們給他一個資料夾名稱了!那他會怎麼做呢?

請看第25行,這段會去抓你給的雲端資料夾的名稱,抓到有就會回傳給你id,沒有就會回傳給你None

之後在第31行部分,我們將取得到的id或是None給他,因為30~31行主要是執行上傳檔案的功能。

def main(is_update_file_function=False, update_drive_service_folder_name=None, update_drive_service_name=None, update_file_path=None):
    """
    :param update_drive_service_folder_name: 給要上傳檔案到雲端的資料夾名稱,預設則是上傳至雲端目錄
    :param is_update_file_function: 判斷是否執行上傳的功能
    :param update_drive_service_name: 要上傳到雲端上的檔案名稱
    :param update_file_path: 要上傳檔案的位置以及名稱
    """

    print("is_update_file_function: %s" % is_update_file_function)
    print("update_drive_service_folder_name: %s" % update_drive_service_folder_name)

    store = file.Storage('token.json')
    creds = store.get()
    if not creds or creds.invalid:
        flow = client.flow_from_clientsecrets('credentials.json', SCOPES)
        creds = tools.run_flow(flow, store)
    service = build('drive', 'v3', http=creds.authorize(Http()))
    print('*' * 10)

    if is_update_file_function is True:
        print(update_file_path + update_drive_service_name)
        print("=====執行上傳檔案=====")
        # 清空 雲端垃圾桶檔案
        # trashed_file(service=service, is_delete_trashed_file=True)
        get_folder_id = search_folder(service = service, update_drive_folder_name = update_drive_service_folder_name)
        # 搜尋要上傳的檔案名稱是否有在雲端上並且刪除
        search_file(service=service, update_drive_service_name=update_drive_service_name,
                    is_delete_search_file=True)
        # 檔案上傳到雲端上
        update_file(service=service, update_drive_service_name=update_drive_service_name,
                    local_file_path=os.getcwd() + '/' + update_drive_service_name, update_drive_service_folder_id=get_folder_id)
        print("=====上傳檔案完成=====")

上傳檔案到Google Drive 指定資料夾程式碼 search_folder 程式碼區塊 說明

什麼情況下才會跑這區塊呢?

當你有給雲端資料夾名稱時,才會執行這塊程式碼,如果沒有就直接回傳None

我們直接看第11~12行,這段的意思是我們現在要開始找雲端上的所有檔案名稱以及資料夾

那我們要開始做篩選的動作 q= 後面這串都是篩選的條件

def search_folder(service, update_drive_folder_name=None):
    """
    如果雲端資料夾名稱相同,則只會選擇一個資料夾上傳,請勿取名相同名稱
    :param service: 認證用
    :param update_drive_folder_name: 取得指定資料夾的id,沒有的話回傳None,給錯也會回傳None
    :return:
    """
    get_folder_id_list = []
    print(len(get_folder_id_list))
    if update_drive_folder_name is not None:
        response = service.files().list(fields="nextPageToken, files(id, name)", spaces='drive',
                                       q = "name = '" + update_drive_folder_name + "' and mimeType = 'application/vnd.google-apps.folder' and trashed = false").execute()
        for file in response.get('files', []):
            # Process change
            print('Found file: %s (%s)' % (file.get('name'), file.get('id')))  # 找到的符合的檔案印出名稱以及ID
            get_folder_id_list.append(file.get('id'))  # 將取得的id放置在一個list內,為何是list 因為可能資料夾有多個相同名稱,但id都不同,因此最後會將你要上傳的檔案全部都放置到相同名稱的資料夾內
        if len(get_folder_id_list) == 0:
            print("你給的資料夾名稱沒有在你的雲端上!,因此檔案會上傳至雲端根目錄")
            return None
        else:
            return get_folder_id_list
    return None

篩選條件程式碼 介紹

  • name = update_drive_folder_name # 指定的名稱是什麼
  • and mimeType = ‘application/vnd.google-apps.folder’ # 和 必須為資料夾
  • and trashed = false # 和 我不要垃圾桶的任何資料

上傳檔案到Google Drive 指定資料夾程式碼 update_file 程式碼區塊 說明

這邊是上傳檔案到雲端的程式碼,指定上傳到某個資料夾的參數也就是 update_drive_service_folder_id 

如果有就會給一個id,沒有則是給None那該怎麼做呢?

第11~16行部份,如果為None的話,我們則直接將檔案給 file_metadata 也就是說 我只要上傳這個檔案,請幫我放在目錄就好!

那如果你有給資料夾的id呢?

就可以看到有多加一個key(parents),代表的是說 我要上傳檔案,請將把檔案放置到這幾個資料夾內 (資料夾id,list的格式)

def update_file(service, update_drive_service_name, local_file_path, update_drive_service_folder_id):
    """
    將本地端的檔案傳到雲端上
    :param update_drive_service_folder_id: 判斷是否有 Folder id 沒有的話,會上到雲端的目錄
    :param service: 認證用
    :param update_drive_service_name: 存到 雲端上的名稱
    :param local_file_path: 本地端的位置
    :param local_file_name: 本地端的檔案名稱
    """
    print("正在上傳檔案...")
    if update_drive_service_folder_id is None:
        file_metadata = {'name': update_drive_service_name}
    else:
        print(update_drive_service_folder_id)
        file_metadata = {'name': update_drive_service_name,
                         'parents': update_drive_service_folder_id}

    media = MediaFileUpload(local_file_path, )
    file_metadata_size = media.size()
    start = time.time()
    file_id = service.files().create(body=file_metadata, media_body=media, fields='id').execute()
    end = time.time()
    print("上傳檔案成功!")
    print('雲端檔案名稱為: ' + str(file_metadata['name']))
    print('雲端檔案ID為: ' + str(file_id['id']))
    print('檔案大小為: ' + str(file_metadata_size) + ' byte')
    print("上傳時間為: " + str(end-start))

    return file_metadata['name'], file_id['id']

注意:你要上傳的檔案名稱,會在執行程式碼的時候,會自動刪除雲端上相同的檔案名稱,在上傳你本地端的檔案

以上是 Python 上傳到 Google Drive 指定資料夾的方式

如果有寫不好的地方請見諒,主要是參考Google Drive文檔以及自己思考架構。

文章內的程式碼都可以直接複製下來練習操作,或是直接套用到自己的專案都行!

多練習,對自已會有很大的幫助!

一回生二回熟,將所學習到內容變成自己的知識。

人就是要學習自己不會的事情,這樣才會不斷的成長、進步!

更多技術文章

透過下方按鈕找尋到相關的技術文章,希望可以幫助到正在學習的你

html 教學文章
Python 教學文章
Ngrok 教學
Jenkins 教學
WordPress 教學
Chatbot 教學

加入到我的粉絲專頁,不定期發布最新文章資訊!
有任何文章問題都可以詢問喔!

測試先生

文章分類: Google Drive 文章標籤: google ap, google drive, google drive api, google drive to specify folder, google drive update file © 2021–2022

主要資訊欄

  • 電子郵件
  • Facebook
  • GitHub
  • LinkedIn
  • 使用 Python 上傳檔案到 Google Drive 指定資料夾 完整程式碼
    • Example 1: 先建立一個資料夾名稱為TestAPI,檔案會上傳到這個資料夾內
    • Example 2: 建立一個TestAPI資料夾裡面再建立一個TestAPI資料夾,兩個資料夾都會有上傳的檔案 Error
    • Example 3: 建立一個TestAPI123資料夾,裡面又建立一個TestAPI資料夾,這時檔案只會上傳到TestAPI,不會上傳到TestAPI123
  • 上傳檔案到Google Drive 指定資料夾程式碼 說明
    • is_update_file_function
    • update_drive_service_folder_name
      • 資料夾名稱相同
      • 放置檔案到資料夾底下的資料夾
    • update_drive_service_name
    • update_file_path
  • 上傳檔案到Google Drive 指定資料夾程式碼 main 程式碼區塊 說明
  • 上傳檔案到Google Drive 指定資料夾程式碼 search_folder 程式碼區塊 說明
    • 篩選條件程式碼 介紹
  • 上傳檔案到Google Drive 指定資料夾程式碼 update_file 程式碼區塊 說明
  • 更多技術文章

Footer

  • 隱私權政策
  • 免責聲明與使用條款
  • 聯絡我們
  • 電子郵件
  • Facebook
  • GitHub
  • LinkedIn

Copyright © 2022 · Magazine Pro on Genesis Framework · WordPress · 登入