Pregunta Escribir archivos en la cuenta de Dropbox desde GAE


Intento crear archivos en una carpeta de Dropbox.com desde una aplicación GAE. Hice todos los pasos para registrar una aplicación de Dropbox e instalé el SDK de Python desde Dropbox localmente en mi máquina de desarrollo. (ver la API de dropbox.com). Todo funciona perfectamente cuando uso el script de prueba cli_client.py en el SDK de Dropbox en mi máquina local para acceder a Dropbox, puedo poner archivos, etc.

Ahora quiero comenzar a trabajar en el entorno de GAE, así que las cosas se vuelven un poco complicadas. Alguna ayuda sería útil.

Para aquellos familiarizados con el código API de Dropbox, tuve los siguientes problemas hasta el momento:

Número 1

El módulo API rest.py Dropbox utiliza pkg_resources para obtener los certs instalados en paquetes de sitio de una instalación de máquina local. Reemplacé

TRUSTED_CERT_FILE = pkg_resources.resource_filename(__name__, 'trusted-certs.crt')

con

TRUSTED_CERT_FILE = file('trusted-certs.crt')

y coloqué el archivo cert en mi directorio de aplicaciones GAE. Quizás esto no es del todo correcto; ver mi código de error de autenticación a continuación.

Número 2

los session.py El módulo API Dropbox usa oauth módulo, así que cambié el include para appengine oauth.

Pero planteó una excepción que GAET oauth no tiene OAuthConsumer método utilizado por el módulo de Dropbox session.py. Así que descargué Oauth 1.0 y lo agregué a mi aplicación y ahora importé esto en lugar de GAE oauth.

Número 3

El módulo GAE ssl no parece tener la propiedad CERT_REQUIRED.

Esto es una constante, así que cambié

self.cert_reqs = ssl.CERT_REQUIRED

a

self.cert_reqs = 2

Esto se usa cuando se llama

ssl.wrap_socket(sock, cert_reqs=self.cert_reqs, ca_certs=self.ca_certs)

Error de autenticación

Pero todavía no puedo conectarme a Dropbox:

Status: 401
Reason: Unauthorized
Body: {"error": "Authentication failed"}
Headers: [('date', 'Sun, 19 Feb 2012 15:11:12 GMT'), ('transfer-encoding', 'chunked'), ('connection', 'keep-alive'), ('content-type', 'application/json'), ('server', 'dbws')]

15
2018-02-19 15:50


origen


Respuestas:


Aquí está mi versión parcheada de Dropbox Python SDK 1.4 que funciona bien para mí con Python 2.7 GAE: dropbox_python_sdk_gae_patched.7z.base64. No se necesitan bibliotecas adicionales de terceros, solo las proporcionadas por el entorno de GAE.

Solo se prueba la carga de archivos (put_file). Aquí están los pasos de configuración:

  1. Desempaquete el archivo en la carpeta raíz de la aplicación GAE (si la aplicación principal está en la carpeta raíz). Puedes decodificar BASE64 usando Encoder / Decoder Base64: base64.exe -d dropbox_python_sdk_gae_patched.7z.base64 dropbox_python_sdk_gae_patched.7z.
  2. Configure APP_KEY, APP_SECRET, ACCESS_TYPE, ACCESS_TOKEN_KEY, ACCESS_TOKEN_SECRET. Los primeros tres se configuran en el momento de creación de la aplicación dropbox. Los dos últimos se obtienen al otorgar acceso a la aplicación a una cuenta específica de Dropbox, puede obtenerlos a través de cli_client.py (desde DB Python SDK) desde el archivo token_store.txt.
  3. Use en el código de esta manera:

    import dropbox
    # ...
    def DropboxUpload(path, data):
        sess = dropbox.session.DropboxSession(APP_KEY, APP_SECRET, ACCESS_TYPE)
        sess.set_token(ACCESS_TOKEN_KEY, ACCESS_TOKEN_SECRET)
        cli = dropbox.client.DropboxClient(sess)
        data_file = StringIO.StringIO(data)
        return cli.put_file(path, data_file)
    # ...
    import json
    class DropboxUploadHandlerExample(webapp2.RequestHandler):
        def get(self):
            url = "http://www.google.com/"
            result = urlfetch.fetch(url)
            self.response.headers['Content-Type'] = 'application/json'
            self.response.out.write(json.dumps(DropboxUpload('/fetch_result.dat', result.content)))
    

7
2018-04-22 00:04



Subí con éxito de Google Appengine a Dropbox con mi propia versión parchada del SDK de Dropbox: https://github.com/cklein/dropbox-client-python

El uso de urllib2 fue reemplazado por huTools.http: https://github.com/hudora/huTools/

Este es el código que se llama en un controlador de solicitud:

    db_client = dropbox.get_dropbox_client(consumer_key='', consumer_secret='', access_token_key='', access_token_secret='')
    fileobj = StringIO.StringIO(data)
    path = '/some/path/filename'
    resp = db_client.put_file(path, fileobj)
    fileobj.close()

3
2018-03-01 14:53



A partir de abril de 2016, ninguna de las otras sugerencias funciona. (API de Dropbox versión 2, Python SDK versión 6.2).

Si solo necesita algunas de las funciones del SDK, me resultó más fácil usar directamente la API HTTP:

def files_upload(f, path, mode='add', autorename=False, mute=False):

    args = {
        'path': path,
        'mode': mode,
        'autorename': autorename,
        'mute': mute,
    }

    headers = {
        'Authorization': 'Bearer {}'.format(ACCESS_TOKEN),
        'Dropbox-API-Arg': json.dumps(args),
        'Content-Type': 'application/octet-stream',
    }

    request = urllib2.Request('https://content.dropboxapi.com/2/files/upload', f, headers=headers)
    r = urllib2.urlopen(request)

2
2018-04-29 12:35



He corregido el Dropbox Python SDK versión 2.2 para trabajar en Google App Engine. Por favor encuentre el código relevante aquí:

https://github.com/duncanhawthorne/gae-dropbox-python

El parche de código relevante (copiado de github) para rest.py está aquí:

 import io
 import pkg_resources
-import socket
+#import socket
 import ssl
 import sys
 import urllib
+import urllib2

+def mock_urlopen(method,url,body,headers,preload_content):
+    request = urllib2.Request(url, body, headers=headers)
+    r = urllib2.urlopen(request)
+    return r         
+    
 try:
     import json
 except ImportError:
 @@ -23,7 +29,10 @@

 SDK_VERSION = "2.2.0"

-TRUSTED_CERT_FILE = pkg_resources.resource_filename(__name__, 'trusted-certs.crt')
+try:
+    TRUSTED_CERT_FILE = pkg_resources.resource_filename(__name__, 'trusted-certs.crt')
+except:
+    TRUSTED_CERT_FILE = file('trusted-certs.crt')


 class RESTResponse(io.IOBase):
 @@ -125,6 +134,7 @@ def flush(self):
         pass

 def create_connection(address):
+    return
     host, port = address
     err = None
     for res in socket.getaddrinfo(host, port, 0, socket.SOCK_STREAM):
 @@ -152,7 +162,7 @@ def json_loadb(data):


 class RESTClientObject(object):
-    def __init__(self, max_reusable_connections=8, mock_urlopen=None):
+    def __init__(self, max_reusable_connections=8, mock_urlopen=mock_urlopen):
         """
         Parameters
             max_reusable_connections
 @@ -206,7 +216,7 @@ def request(self, method, url, post_params=None, body=None, headers=None, raw_re
                 raise ValueError("headers should not contain newlines (%s: %s)" %
                                  (key, value))

-        try:
+        if True:
             # Grab a connection from the pool to make the request.
             # We return it to the pool when caller close() the response
             urlopen = self.mock_urlopen if self.mock_urlopen else self.pool_manager.urlopen
 @@ -217,14 +227,14 @@ def request(self, method, url, post_params=None, body=None, headers=None, raw_re
                 headers=headers,
                 preload_content=False
             )
-            r = RESTResponse(r) # wrap up the urllib3 response before proceeding
-        except socket.error as e:
-            raise RESTSocketError(url, e)
-        except urllib3.exceptions.SSLError as e:
-            raise RESTSocketError(url, "SSL certificate error: %s" % e)
+            #r = RESTResponse(r) # wrap up the urllib3 response before proceeding
+        #except socket.error as e:
+        #    raise RESTSocketError(url, e)
+        #except urllib3.exceptions.SSLError as e:
+        #    raise RESTSocketError(url, "SSL certificate error: %s" % e)

-        if r.status not in (200, 206):
-            raise ErrorResponse(r, r.read())
+        #if r.status not in (200, 206):
+        #    raise ErrorResponse(r, r.read())

         return self.process_response(r, raw_response)

 @@ -321,10 +331,11 @@ def PUT(cls, *n, **kw):
         return cls.IMPL.PUT(*n, **kw)


-class RESTSocketError(socket.error):
+class RESTSocketError():
     """A light wrapper for ``socket.error`` that adds some more information."""

     def __init__(self, host, e):
+        return
         msg = "Error connecting to \"%s\": %s" % (host, str(e))
         socket.error.__init__(self, msg)

1
2017-08-17 18:28