Невозможно экспортировать слайд Google в формате PDF на стороне сервера

Вот мой код для тестирования экспорта файлов с диска Google на стороне сервера.

import logging

from flask import Flask, render_template, request

from googleapiclient.discovery import build
from googleapiclient.http import MediaIoBaseDownload
from oauth2client.client import AccessTokenCredentials

import httplib2
import io


app = Flask(__name__)


@app.route('/gdrive/selectcallback')
def userselectioncallback():
    print "In user selection callback... "

    code = request.args.get('user_token')
    fileId = request.args.get('fileId')

    credentials = AccessTokenCredentials(code,
                                         'my-user-agent/1.0')


    http = httplib2.Http()
    http_auth = credentials.authorize(http)

    drive_service = build('drive', 'v3', http=http_auth)

    drive_request = drive_service.files().export(
        fileId=fileId,
        mimeType='application/pdf')
    fh = io.FileIO('test.pdf', 'wb')
    downloader = MediaIoBaseDownload(fh, drive_request)
    done = False
    while done is False:
        status, done = downloader.next_chunk()
        print "Download %d%%." % int(status.progress() * 100)

    return code


if __name__ == '__main__':
    # This is used when running locally. Gunicorn is used to run the
    # application on Google App Engine. See entrypoint in app.yaml.
    app.run(host='127.0.0.1', port=8090, debug=True)

На стороне веб-клиента, когда пользователь выбирает файл в средстве выбора файлов, внешний интерфейс javascript вызывает /gdrive/selectcallback в приведенном выше коде Python с токеном и идентификатором файла.

Например, токен выглядит примерно так: ya29.Glu5BG-LQJFqZ-e4uImMSxz-14iS41jVLfXk6rVKvAPjylCwhUh98ZJk1iIC5Eb49pTfflGnU6qE7uzK44AYr0Wn79QMUkF368WFaYrhidrvpVjcsJSZ9P1M8VU6 а идентификатор файла выглядит примерно так 1ON9kGyb02TFCygy8jeIYyo2BKj5SzKgAP0xi5Rm08D4

Вот соответствующий интерфейсный код (в coffeescript):

  pickerCallback = () ->
    view   = new google.picker.View(google.picker.ViewId.PRESENTATIONS)
    picker = new google.picker.PickerBuilder()
      .enableFeature(google.picker.Feature.NAV_HIDDEN)
      .setAppId('zeetings')
      .setOAuthToken(oauthToken)
      .addView(view)
      .setDeveloperKey(env['googleapi-client'].apiKey)
      .setCallback(selectCallback)  # The callback calls the python backend
      .build()
    picker.setVisible true

  selectCallback = (data) ->
    if data.action is google.picker.Action.PICKED
      fileId = data.docs[0].id
      fileSelectedCallback(fileId, oauthToken) if fileSelectedCallback

Основываясь на информации об отладке, мой код Python выдает эти два вызова https:

01.09.2017, 11:32:38,810 pid 260 tid 140546358265600 INFO
запрашиваемый URL-адрес обнаружения: GET https://www.googleapis.com/discovery/v1/apis/drive/v3/rest

01.09.2017 11:32:39,009 pid 260 tid 140546358265600 Запрашиваемый URL-адрес обнаружения ИНФОРМАЦИИ: GET https://www.googleapis.com/drive/v3/files/1ON9kGyb02TFCygy8jeIYyo2BKj5SzKgAP0xi4Rm08D4/export?2Fpdf

Если я использую второй URL-адрес непосредственно в браузере, я получаю следующую ошибку:

{
 "error": {
  "errors": [
   {
    "domain": "usageLimits",
    "reason": "dailyLimitExceededUnreg",
    "message": "Daily Limit for Unauthenticated Use Exceeded. Continued use requires signup.",
    "extendedHelp": "https://code.google.com/apis/console"
   }
  ],
  "code": 403,
  "message": "Daily Limit for Unauthenticated Use Exceeded. Continued use requires signup."
 }
}

(Я не думаю, что приведенное выше сообщение об ошибке на самом деле отражает основную причину. Скорее всего, это связано с тем, что вызов не аутентифицирован в моем браузере.)

Я подозреваю, что мне нужно использовать библиотеку google-auth (https://google-auth.readthedocs.io/en/latest/user-guide.html#making-authenticated-requests), но я не знаю, как соединить google-auth с кодом Python, который у меня есть выше. Я полагаю, что могу получить учетные данные через

from google.oauth2 import service_account

credentials = service_account.Credentials.from_service_account_file(
    '/path/to/key.json')

Но что мне делать с credentials после этого? Могу ли я использовать его, чтобы полностью заменить credentials = AccessTokenCredentials(code,'my-user-agent/1.0')?

P.S.

Согласно предложению @Tanaike, я попытался напрямую использовать URL-адрес API. Это результат, который я получил:

{
 "error": {
  "errors": [
   {
    "domain": "global",
    "reason": "fileNotDownloadable",
    "message": "Only files with binary content can be downloaded. Use Export with Google Docs files.",
    "locationType": "parameter",
    "location": "alt"
   }
  ],
  "code": 403,
  "message": "Only files with binary content can be downloaded. Use Export with Google Docs files."
 }
}

Кажется, проблема с v3 API. Если я переключусь на v2 и воспользуюсь ссылкой downloadUrl, я смогу скачать файл в формате pdf.


comment
какое сообщение об ошибке вы получаете от API экспорта? Экспорт через проводник API работает нормально   -  person Maurice Codik    schedule 02.08.2017
comment
Это происходит с конкретным слайдом, как указано в вопросе (ссылка на который также предоставляется). Загрузка 0% распечатывалась много раз, пока в конце не было поймано 500   -  person Anthony Kong    schedule 03.08.2017
comment
Возможно, вы захотите проверить сообщение об ошибке, из-за которой Drive API возвращает ошибку 500. Это не было ясно, работает ли он сейчас для версии 3, поскольку отчет только подтвердил, что он работает в версии 2. Возможно, вы захотите следить за проблемой, а затем прокомментировать/создать новую. Надеюсь, это поможет   -  person Mr.Rebot    schedule 03.08.2017
comment
Когда он найдет токен обновления и идентификатор файла, вы можете использовать Drive API. В вашем скрипте можно найти идентификатор файла. Я хотел бы подтвердить токен обновления. Что такое code = request.args.get('user_token')? Кажется, это может быть токен обновления. если это токен обновления, могу ли я спросить вас о области действия, включенной в токен обновления? И вы уже включили Drive API в консоли API?   -  person Tanaike    schedule 04.09.2017
comment
@Tanaike Я добавил к вопросу некоторый внешний код и просто обнаружил проблему с моим кодом coffeescript: я мог передать токен oauth2 вместо кода доступа к файлу на серверную часть. Я проверю, является ли это основной причиной.   -  person Anthony Kong    schedule 04.09.2017
comment
Если у вас есть пригодный для использования токен доступа, вы можете подтвердить области с помощью этой команды curl. curl -L --data "access_token=### access token ###" https://www.googleapis.com/oauth2/v2/tokeninfo   -  person Tanaike    schedule 04.09.2017
comment
@Tanaike Странно то, что токен иногда работает. Он работает с некоторыми файлами моего диска, но не с другими. Спасибо за очень полезный совет, буду пробовать   -  person Anthony Kong    schedule 04.09.2017
comment
Информация о токене выглядит нормально, хотя я думаю, что она неверна, потому что она исходит от gapi.auth, а не picker: { "issued_to": "534888811371-fegdtjm9ak4a6nhq2f4ukrjfkgdnv0q6.apps.googleusercontent.com", "audience": "534888811371-fegdtjm9ak4a6nhq2f4ukrjfkgdnv0q6.apps.googleusercontent.com", "scope": "https://www.googleapis.com/auth/drive https://www.googleapis.com/auth/drive.file", "expires_in": 3309, "access_type": "online" }   -  person Anthony Kong    schedule 04.09.2017
comment
Спасибо, что подтвердили это. Этот токен доступа в порядке. Так, например, как насчет использования Drive API на picker с использованием токена обновления, который извлекает токен доступа?   -  person Tanaike    schedule 04.09.2017
comment
Я получил это Только файлы с двоичным содержимым могут быть загружены. ошибка. Мне кажется, проблема с Google Drive v3 API. (Я обновил вопрос с сообщением об ошибке)   -  person Anthony Kong    schedule 04.09.2017
comment
Ошибка указывает на то, что вы можете попытаться загрузить файлы, кроме Google Docs, с помощью экспорта. Если вы хотите загрузить файлы, за исключением файлов Google Docs, их необходимо загрузить с помощью files.get, используя запрос alt=media. developers.google.com/drive/v3/reference/files/get   -  person Tanaike    schedule 04.09.2017
comment
Я хочу экспортировать слайд Google. Я думаю, что документ API предлагает мне использовать export (developers.google.com/ диск/v3/ссылка/файлы/экспорт). Вот похожее видео на YouTube от команды Google Drive: youtu.be/-7YH6rdR-tk?t =10м33с   -  person Anthony Kong    schedule 04.09.2017


Ответы (1)


Пользователь @Tanaike дал мне много хороших советов по отладке этой проблемы. Я смог напрямую протестировать REST API, чтобы убедиться, что 1) у меня есть правильный код доступа и 2) API экспорта файлов v3 работает должным образом.

Оказывается, проблема связана с классом MediaIoBaseDownload. Если я уберу его из кода и просто получу данные напрямую:

data = drive_service.files().export(
        fileId=fileId,
        mimeType='application/pdf').execute()
f = open('test.pdf)
f.write(data)
f.close()

тогда он работает так, как ожидалось

person Anthony Kong    schedule 05.09.2017