Упростить обмен файлами

Простой обмен файлами

Пример кода для работы с общими папками Google Drive в совместных проектах

Недавно снова возникла проблема с обменом данными, и я подумал, что сейчас самое время разработать метод для работы с общими папками. Я работаю независимым профессионалом в области геоинформационных наук и часто взаимодействую с различными организациями одновременно. В своих проектах я заметил, что каждая организация имеет свой уникальный подход к работе с данными, определяемый своей культурой и рабочей этикой, что приводит к разнообразию методологий. К счастью, у них есть некоторые общие практики, и одна из них – работа с облачной системой управления данными, часто Google, но также может быть One-Drive (от Microsoft) или Dropbox.

В этой статье я объясню, как использовать Python с общими папками в экосистеме Google.

Фото Анни Спратт на Unsplash

Практическое применение

Управление файлами на локальной машине – это очень индивидуально и (надеюсь) стандартизовано, или по крайней мере имеет некоторую стандартизацию, когда вы работаете в организации. Обмен файлами между системами может быть сложным, но работа с общими папками – это вариант, когда у вас нет прямого доступа к рабочей папке, и организация может предоставить вам специально назначенную рабочую папку для обмена файлами. В этом примере организация предоставила доступ к папке с названием DATA в их хранилище Google Drive, и договорились, что мы можем использовать эту папку для обмена файлами.

Управление локальными файлами

Для объяснения быстро, для тех, кто не знаком с обменом файлами в Google Drive, процесс начинается с получения электронного письма с приглашением внести свой вклад в конкретную папку; см. приглашение (слева) ниже. В приглашении есть кнопка, которая открывает веб-браузер с интерфейсом Google Drive (справа), связанным с Google-почтой получателя приглашения.

Рисунок 1: Создание общей папки (изображение автора)

В интерфейсе есть несколько важных сведений, и знание их заранее поможет вам в дальнейшем процессе.

  • В URL (вверху экрана) есть зашифрованный идентификатор, который Google использует для отслеживания всех операций с этой папкой, и этот идентификатор мы получим в коде Python позже в этой статье.
  • Затем говорится: “Совместное использование со мной” и название общей папки; это также важно, потому что, когда мы монтируем Google Drive в блокноте CoLab, мы увидим, что эта категория недоступна.
  • И, наконец, мы видим файлы и папки в папке Data; это означает, что мы можем получить доступ к нужной информации и добавлять новые файлы в папку. Однако могут возникнуть проблемы с настройками безопасности папки, поэтому хорошим тестом на этом этапе является создание небольшого текстового файла и перетаскивание его в папку “ExternalData”, чтобы проверить, что у вас есть полный доступ.

Чтобы сделать папку “Совместное использование со мной” доступной, нам нужно связать эту папку с локальным/личным диском. Мы можем сделать это, создав ярлык, но это ручной шаг и будет отличаться для каждого. Чтобы получить доступ к общей папке или файлу в Google Colab:

  1. Перейдите в раздел “Совместное использование со мной” в Google Drive.
  2. Выберите папку или файл, к которому вы хотите получить доступ.
  3. Щелкните правой кнопкой мыши на нем и выберите “Добавить ярлык к диску”, и появится всплывающее окно “Выбрать Мой Диск”, затем нажмите “Добавить ярлык”.
  4. Разместите ярлык в месте на вашем диске, где вы сможете его легко найти; в используемой мной настройке местоположение для ярлыков – “__Shared”, чтобы папка с ярлыками была вверху списка папок в разделе “Мой Диск”, а затем подкаталог для организации.
  5. Переименуйте ярлык в осмысленное имя; в этом примере я использую “DataDevelopement”. Местоположение файла и соглашения о его названии очень индивидуальны, и для работы программы не имеет значения, где файлы хранятся и как они называются, но наличие некоторой структуры может избежать некоторых проблем в дальнейшем.
Рисунок 2: Создание ярлыка (изображение автора)

С организованной локальной файловой системой и настроенным личным Google Drive, мы можем попытаться работать с этой общей папкой в блокноте Python и автоматизировать обмен файлами в проекте.

Установка

Этот проект основан на блокноте Google Colab или “Collaboratory”, который я поделюсь в конце этой статьи. Преимущество использования этой среды заключается в том, что она позволяет вам писать и выполнять код Python в браузере со следующими преимуществами:

  • Отсутствие необходимости в настройке
  • Бесплатный доступ к графическим процессорам
  • Простое совместное использование

Это очень важные моменты при работе с организациями, у которых есть свои внутренние процедуры, потому что, как внешний сотрудник, у вас часто нет прямого доступа к кодовой базе (и это может иметь множество причин, от вопросов безопасности до ограничений управления проектом). Блокнот Colab является частью экосистемы Google и (как дополнительное преимущество) создает среду выполнения с возможностью подключения личных Google-дисков (для обмена файлами).

Импортирование модулей и пакетов

В этом примере в блокнот загружаются только необходимые пакеты, и нам нужно несколько конкретных библиотек для работы с общим диском.

Авторизация Google

from oauth2client.client import GoogleCredentialsfrom google.colab import auth as google_authgoogle_auth.authenticate_user()from google.colab import drivedrive.mount('/content/gdrive')

Использование oauth2client и учетных данных Google упростит работу с файлами. Существуют альтернативы, например, скачивание файла JSON с учетными данными, и будут ситуации, когда работа с файлом JSON будет предпочтительнее использования учетных данных Google, но поскольку это проект без конфиденциальных данных, библиотека oauth2client обеспечивает достаточную защиту.

pydrive

from pydrive.auth import GoogleAuthfrom pydrive.drive import GoogleDrivegauth = GoogleAuth()gauth.credentials = GoogleCredentials.get_application_default()drive = GoogleDrive(gauth)

pydrive – это оболочечная библиотека для google-api-python-client, которая упрощает множество общих задач API Google Drive, и одна из этих функциональностей – обработка ответов при запросе файловой системы Google Drive. Google Drive хранит все объекты по идентификаторам, и идентификаторы связаны реляционной информацией в объектах. Возможно получить доступ к этой информации через API (см. следующий блок кода), но оболочка выполняет всю громоздкую работу для нас, когда мы создаем экземпляр GoogleDriveFileList с параметрами Files.list() в виде словаря. Вызов GetList() получит все файлы, соответствующие вашему запросу, в виде списка GoogleDriveFile.

Клиент Google API

# Google API client:from googleapiclient.discovery import build# Инициализация клиента Google Drive APIdrive_service = build('drive', 'v3')

Клиент Google API – это большая библиотека со множеством функциональностей, но для этого проекта нам нужен только один модуль: build. Модуль build создает объект ресурса для взаимодействия с API и возвращает методы для взаимодействия с сервисом. Библиотека pydrive хорошо справляется с основными функциями, такими как создание, обновление и удаление файлов, но в некоторых случаях (в этом проекте) нам требуется более продвинутая функциональность, и доступ к “сервису” позволяет нам извлекать информацию, которую методы pydrive не улавливают.

Это завершает настройку блокнота. В этом примере нам не нужны другие библиотеки, кроме загруженных библиотек для управления файлами, и с загруженными библиотеками мы можем посмотреть, что они делают.

Управление файлами в блокноте

До этого момента произошло несколько вещей:

  • Настроена авторизация Google,
  • Создан доступ к диску (для чтения/записи), и
  • Пакет Pydrive доступен для навигации по диску

Надеюсь, когда вы следуете инструкциям и запускаете код, вы увидите изображение справа, после обновления панели. Вы можете увидеть ярлык на изображении в виде папки под “__Shared”, и мы не видим раздел “Общий доступ ко мне”, но поскольку у нас есть ярлык, нам не нужно видеть файлы “Общий доступ ко мне”.

Рисунок 3: Неподключенное и подключенное состояния среды выполнения в веб-интерфейсе Google Colab (изображение автора)

Google Drive работает по-другому, чем управление файлами в локальных операционных системах, физическое расположение файлов не имеет значения, потому что объекты управляются по ID в неструктурированном DataLake, и мы можем получить доступ к файлам и папкам по ID.

К сожалению, хотя в Python есть функции walk в модуле os.path для обхода файловой системы, подобного метода не существует для Google Drive (или я не знаю об этом методе). Однако мы можем использовать библиотеку pydrive и вручную обходить папки в дереве каталогов, и к счастью, мы знаем, куда мы хотим попасть по пути к папке. Таким образом, нам не нужно просматривать всю структуру, но мы можем использовать имена папок пути данных, чтобы углубиться в дерево папок.

Итак, мы перебираем маленький список (в этом примере три элемента), чтобы найти ID и использовать этот ID для перехода на следующий уровень. Обратите внимание, что четвертый уровень закомментирован; мы дойдем до этого уровня во второй части раздела работы с файлами в этой записной книжке.

# Тестирование обработки файлов:# В этом примере есть три уровня папок:# /content/gdrive/MyDrive/__Shared/<your Project>/DataDevelopment# Обновите их в соответствии со структурой:folderList1 = ["__Shared", your_Project ,"DataDevelopment"] #, "ExternalData"]

Цикл в кодовом блоке ниже начинается в корне, и когда он находит элемент в списке, цикл будет использовать ID объекта для перехода на следующий уровень в списке, и если элемент не найден, код будет сообщать, что папка не найдена, и не будет искать другие папки глубже в структуре. Цикл заканчивается либо ID папки ярлыка, либо сообщает, что папка не найдена.

# Попытка копирования созданного фиктивного файла:boo_foundFolder = FalsefileID = "root"level = 0# Просмотр всех папок и файлов в вашем Google Drive# Сначала переберите список:print("Структура файлов и папок - проверка с ID")for folderName in folderList1:  print(f"Проверка: {folderName}")  if boo_foundFolder or fileID == "root": #первый запуск    boo_foundFolder = False    fileList = drive.ListFile({'q': f"'{fileID}' in parents and trashed=false"}).GetList()    for file in fileList:      # Тестирование имени файла:            if(file['title'] == folderName):        fileID = file['id']        boo_foundFolder = True        level += 1       # end if    # end for    if boo_foundFolder == False:      print(f"папка не найдена")      break    # end if      # end if# end forprint(f"Найдена ли папка: {boo_foundFolder}")if boo_foundFolder:  print(fileID)  ShortCutID = fileIDelse:  ShortCutID = 0

На данный момент у нас есть локальный идентификатор файла для рабочей папки, но прежде чем мы сможем искать файлы в этом месте, нам нужно сопоставить этот локальный идентификатор с целевым идентификатором общей папки. Чтобы найти эту информацию, нам нужен помощник: drive_service. Мы активировали помощник при загрузке проекта, и не получили предупреждения, что означает, что у нас есть доступ к службе, используя API, и запрашивая информацию по ID.

Детали, которые нам нужны, лучше всего собрать с помощью простой функции, например, функции findTargetID в следующем кодовом блоке. В этой функции fileID – это ID ярлыка, которое мы нашли, перебирая имена в папках, и вызывая drive_service.files().get и указывая поля, мы получаем целевой ID папки (этот ID будет таким же, как в URL веб-интерфейса Google Drive (см. Рисунок 1).

def findTargetID(fileID, drive_service):  # ID общей папки, для которой нужно получить данные ShortcutDetails  file_id = fileID  try:      # Получение информации о файле      file = drive_service.files().get(fileId=file_id,                                       fields="id, shortcutDetails").execute()      # Проверка, является ли файл ярлыком      if 'shortcutDetails' in file:          shortcut_details = file['shortcutDetails']          print("Детали ярлыка:")          print(f"ID цели: {shortcut_details['targetId']}")          print(f"Целевой MIME-тип: {shortcut_details['targetMimeType']}")      else:          print("Файл не является ярлыком.")      # end if  except Exception as e:      print(f"Произошла ошибка: {e}")  return shortcut_details['targetId']if boo_foundFolder:  targetID = findTargetID(fileID, drive_service)  print(targetID)else:  print("Папка не найдена")# end if

С этим идентификатором цели у нас есть доступ к фактической общей папке на сервере данных Google, и мы больше не работаем с ярлычной папкой.

Повторим, причиной создания ярлычной папки была возможность видеть папку в нашем списке примонтированных папок. Категория “Общие со мной” не примонтирована, но ярлыки примонтированы. Так что с этим новым ID мы можем искать файлы.

Поиск файлов

Теперь у нас есть то, что нам нужно, идентификатор цели папки, которой мы поделились с нами в начале процесса, и с этим ID у нас есть доступ ко всем обычным операциям с файлами.

Мы можем проверить, достаточно ли у нас разрешений на общую папку, сначала создав небольшой текстовый файл в среде выполнения; создание этого файла также подтверждает, что у нас есть доступ к среде выполнения, потому что он появится в левой панели веб-интерфейса блокнота CoLab, когда файл будет правильно создан.

# Создание тестового файла:with open('example.txt', 'w') as f:  f.write('Это пример файла для тестирования общего использования файлов CoLab')# этот файл теперь находится в пространстве выполнения блокнота # (см. левую плоскость, под файлами)

Теперь идея заключается в том, чтобы переместить этот файл в папку “Общие со мной” “Данные”, которую мы переименовали в “РазработкаДанных” в ярлычной папке, но функция в предыдущем разделе предоставляла <target ID>, и мы можем использовать этот ID для проверки доступности файла, который мы только что создали в среде выполнения.

if boo_foundFolder:  print("папка найдена")  folderID = targetID  file_on_drive = False  file_id = 0    # проверяем, есть ли файл на диске:  fileList = drive.ListFile({'q': f"'{folderID}' in parents and trashed=false"}).GetList()  for file in fileList:    if(file['title'] == "example.txt"):      file_on_drive = True      fileID = file['id']    # end if  # end for  if file_on_drive:  #Перезаписывает существующий файл Google Drive."""    file1 = drive.CreateFile({'id': fileID})    strFileHandling = "Обновлено"  else:    file1 = drive.CreateFile({"mimeType": "text/csv",                             "parents": [{"kind": "drive#fileLink",                                         "id": folderID}]})    strFileHandling = "Создано"  # end if  # создание привязки к файлу в среде выполнения:  file1.SetContentFile("example.txt")  # копирование файла на Google Drive:  file1.Upload()  print(f'{strFileHandling} файл %s с типом MIME %s' % (file1['title'], file1['mimeType']))else:  print("папка не найдена")# end if

Запуск кода выше создаст новый файл в общей папке или обновит (перезапишет) файл, когда файл будет найден.

Создание рабочей области

Есть вторая причина использовать идентификатор ярлыка для поиска идентификатора цели, а именно поиск элементов в общей папке. Как уже упоминалось ранее, Google Drive управляет всем по ID, а у идентификатора ярлыка нет дочерних элементов, поэтому использование этого ID для поиска новых элементов приведет к пустому списку. Это можно проверить, включив имя папки “ВнешниеДанные” в первый список папок; первый список не найдет эту папку. Однако повторный запуск цикла с использованием идентификатора цели найдет эту папку.

В приведенном ниже фрагменте кода создается новый список папок, используя имена папок под именем “Общие со мной”. Папка “ВнешниеДанные” доступна (см. рисунок 1), но “НоваяПапкаДанных” еще не создана.

# Обновите их в соответствии с вашей структурой:# ... РазработкаДанных/ВнешниеДанные/__CoLab_Блокнот # Установка рабочей папки:folderList2 = ["ВнешниеДанные", "НоваяПапкаДанных"]

Мы можем использовать ту же структуру цикла, что и раньше, но теперь вместо того, чтобы начинать с КОРНЯ, мы начинаем с идентификатора цели, и цикл найдет папку “ВнешниеДанные”, но не новую папку данных.

Поскольку рабочая папка еще не существует, мы можем использовать drive_service.files, чтобы создать эту новую папку и с помощью того же метода передавать все файлы из среды выполнения в папку “Общие со мной”.

def create_folder_in_folder(folder_name,parent_folder_id, drive_service):    file_metadata = {    'name' : folder_name,    'parents' : [parent_folder_id],    'mimeType' : 'application/vnd.google-apps.folder'    }    file = drive_service.files().create(body=file_metadata, supportsAllDrives=True,                                   fields='id').execute()    print ('Идентификатор папки: %s' % file.get('id')) 

if WorkingFolderID == 0:   # fileID is the parent ID from the previous search  create_folder_in_folder("НоваяПапкаДанных", fileID, drive_service)

Основные выводы: Файловая система Google Диска основана на идентификаторах, и у всех объектов есть свои идентификаторы. Объекты «Поделённые со мной» недоступны в Google Colab, но с помощью «Ярлыка» к ним можно получить доступ, и, найдя связанный Идентификатор цели, можно работать непосредственно с папкой «Поделённые со мной», включая объекты в папке, которая изначально была поделена с нами.

Выводы

В этой статье мы рассмотрели некоторые основные аспекты работы с общими папками, включая:

  1. Настройка локального управления файлами: Мы начали процесс с получения приглашения на взаимодействие с определенным каталогом Google Диска и показали, как организовать локальную файловую систему для повышения эффективности совместной работы.
  2. Настройка Google Colab для совместной работы: Мы обсудили преимущества использования Google Colab, совместной среды разработки на языке Python, и показали, как настроить её для совместной работы над проектами.
  3. Импорт необходимых модулей и пакетов: Мы предоставили примеры кода для импорта основных модулей и пакетов, включая авторизацию Google, pydrive для упрощения задач работы с API Google Диска и клиент API Google для расширенного функционала.
  4. Управление файлами в блокноте: Вы увидели, как управлять файлами в среде Google Colab, включая создание и перемещение файлов между локальной средой и общими папками с использованием Идентификатора поделённого и Идентификатора цели.
  5. Поиск файлов и создание рабочих пространств: Мы рассмотрели процесс поиска файлов в общих папках с использованием Идентификаторов целей и создания новых папок и рабочих пространств для ваших проектов.

Надеюсь, этот обзор работы с общими папками и файлами между организациями был полезным и дал некоторые идеи о том, как работать с файлами и папками в общей папке.

Благодарю вас за чтение, и я надеюсь, что этот пост помог вам решить проблему или дал вам идею для следующего проекта.

Ссылка на блокнот Google CoLab: gist

Отказ от ответственности: Код, использованный в этом примере, не оптимизирован, а написан для иллюстрации процесса (любые предложения по улучшению кода приветствуются на страницах gitHub, на которых размещается этот блокнот).