0

Voici mon code pour tester l'exportation de fichiers à partir de google drive côté serveur.Impossible d'exporter une diapositive google au format pdf côté serveur

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) 

Du côté client Web, une fois qu'un utilisateur sélectionne un fichier à partir du sélecteur de fichiers, l'avant javascript appellera le /gdrive/selectcallback dans le code python ci-dessus avec le jeton et id fichier.

À titre d'exemple, le jeton ressemble à quelque chose comme ceci: ya29.Glu5BG-LQJFqZ-e4uImMSxz-14iS41jVLfXk6rVKvAPjylCwhUh98ZJk1iIC5Eb49pTfflGnU6qE7uzK44AYr0Wn79QMUkF368WFaYrhidrvpVjcsJSZ9P1M8VU6 et id fichier ressemble à ceci 1ON9kGyb02TFCygy8jeIYyo2BKj5SzKgAP0xi5Rm08D4

Voici le code frontal concerné (en 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 

Basé sur le des informations de débogage, mon code python émet ces deux appels https:

2017-09-01 11:32:38,810 pid 260 tid 140546358265600 INFO
discovery URL being requested: GET https://www.googleapis.com/discovery/v1/apis/drive/v3/rest

2017-09-01 11:32:39,009 pid 260 tid 140546358265600 INFO discovery URL being requested: GET https://www.googleapis.com/drive/v3/files/1ON9kGyb02TFCygy8jeIYyo2BKj5SzKgAP0xi4Rm08D4/export?mimeType=application%2Fpdf

Si j'utilise directement la deuxième url dans un large owser, j'obtiens l'erreur suivante:

{ 
"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." 
} 
} 

(Je ne pense pas que le message d'erreur ci-dessus reflète réellement la cause profonde. Il est plus probable en raison du fait que l'appel n'est pas authentifié dans mon navigateur.)

Je pense que je dois utiliser la bibliothèque google-auth (https://google-auth.readthedocs.io/en/latest/user-guide.html#making-authenticated-requests), mais je ne suis pas sûr de savoir comment se marier google-auth au code python I avoir ci-dessus. Je suppose que je peux obtenir un titre via

from google.oauth2 import service_account 

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

Mais que dois-je faire avec le credentials après cela? Est-ce que je l'utilise pour remplacer credentials = AccessTokenCredentials(code,'my-user-agent/1.0') entièrement?

P.S.

Selon la suggestion de @ Tanaike, j'ai essayé d'utiliser directement l'URL de l'API. C'est le résultat que j'ai obtenu:

{ 
"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." 
} 
} 

Il semble s'agir d'un problème d'API v3. Si je passe en v2 et utilise un lien downloadUrl, je peux télécharger le fichier en format pdf.

+0

ce message d'erreur que vous obtenez de l'API d'exportation? L'exportation via l'explorateur d'API semble fonctionner correctement –

+0

Il arrive à la diapositive particulière comme mentionné dans la question (un lien est également fourni). "Download 0%" a été imprimé plusieurs fois jusqu'à la fin de la procédure. Un 500 est intercepté. –

+0

Vous pouvez vérifier que cette erreur est signalée par [problème (https://issuetracker.google.com/issues/64294118) Drive API renvoie l'erreur 500 Il n'était pas clair si cela fonctionne maintenant pour v3 puisque le rapport a seulement confirmé qu'il fonctionne dans v2. Vous voudrez peut-être suivre le problème, puis en commenter/en créer un nouveau.Espérons que cette aide –

Répondre

0

L'utilisateur @Tanaike m'a donné beaucoup de bons conseils pour déboguer ce problème. J'ai pu tester directement l'API REST pour vérifier que 1) j'ai le bon code d'accès et 2) l'API d'exportation de fichier v3 fonctionne comme prévu

Il s'avère que le problème est lié à la classe MediaIoBaseDownload. Si je le supprimer du code et simplement recevoir les données directement:

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

il fonctionne comme prévu