Запись файлов в аккаунт Dropbox из GAE - PullRequest
16 голосов
/ 19 февраля 2012

Я пытаюсь создать файлы в папке Dropbox.com из приложения GAE. Я выполнил все шаги, зарегистрировал приложение Dropbox и установил Python SDK из Dropbox локально на моей машине для разработки. (см. dropbox.com API). Все это прекрасно работает, когда я использую тестовый скрипт cli_client.py в Dropbox SDK на моей локальной машине для доступа к Dropbox - могу «положить» файлы и т. Д.

Теперь я хочу начать работать в среде GAE, поэтому все становится немного сложнее. Некоторая помощь будет полезна.

Для тех, кто знаком с кодом Dropbox API, у меня были следующие проблемы:

Выпуск 1

Модуль API rest.py Dropbox использует pkg_resources для получения сертификатов, установленных в пакетах сайта для установки на локальном компьютере. Я заменил

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

с

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

и поместил файл сертификата в мой каталог приложений GAE. Возможно, это не совсем верно; см. мой код ошибки аутентификации ниже.

Выпуск 2

Модуль session.py Dropbox API использует модуль oauth , поэтому я изменил включение на appengine oauth.

Но возникло исключение, что oauth GAE не имеет OAuthConsumer метода, используемого модулем Dropbox session.py. Поэтому я скачал oauth 1.0 и добавил в свое приложение теперь импорт вместо GAE oauth.

Выпуск 3

Модуль GAE ssl, похоже, не имеет свойства CERT_REQUIRED.

Это константа, поэтому я изменил

self.cert_reqs = ssl.CERT_REQUIRED

до

self.cert_reqs = 2

Используется при звонке

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

Ошибка аутентификации

Но я все еще не могу подключиться к 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')]

Ответы [ 4 ]

7 голосов
/ 22 апреля 2012

Вот моя исправленная версия Dropbox Python SDK 1.4, которая хорошо работает для меня с Python 2.7 GAE: dropbox_python_sdk_gae_patched.7z.base64 . Никаких дополнительных сторонних библиотек не требуется, только те, которые предоставляются средой GAE.

Проверяется только загрузка файла (put_file). Вот шаги настройки:

  1. Распакуйте архив в корневую папку приложения GAE (если основное приложение находится в корневой папке). Вы можете декодировать BASE64, используя Base64 Encoder / Decoder : base64.exe -d dropbox_python_sdk_gae_patched.7z.base64 dropbox_python_sdk_gae_patched.7z.
  2. Настройка APP_KEY, APP_SECRET, ACCESS_TYPE, ACCESS_TOKEN_KEY, ACCESS_TOKEN_SECRET. Первые три настраиваются во время создания приложения Dropbox. Последние два получены при предоставлении доступа приложения к определенной учетной записи Dropbox, вы можете получить их через cli_client.py (из SDK БД Python) из файла token_store.txt.
  3. Используйте в коде, подобном этому:

    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)))
    
3 голосов
/ 29 апреля 2016

По состоянию на апрель 2016 года ни одно из других предложений не работает. (Dropbox API версии 2, Python SDK версии 6.2).

Если вам нужны только некоторые функции SDK, проще всего напрямую использовать HTTP API:

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)
3 голосов
/ 01 марта 2012

Я успешно загрузил из Google Appengine в Dropbox свою собственную исправленную версию SDK Dropbox: https://github.com/cklein/dropbox-client-python

Использование urllib2 было заменено на huTools.http: https://github.com/hudora/huTools/

Это код, который вызывается в обработчике запросов:

    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()
1 голос
/ 17 августа 2015

Я исправил Dropbox Python SDK версии 2.2 для работы в Google App Engine. Пожалуйста, найдите соответствующий код здесь:

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

Соответствующий патч (скопированный с github) для rest.py находится здесь:

 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)
...