How to determine the Dropbox folder location programmatically?

最后都变了- 提交于 2019-11-27 09:13:34
c00kiemonster

I found the answer here. Setting s equal to the 2nd line in ~\AppData\Roaming\Dropbox\host.db and then decoding it with base64 gives the path.

def _get_appdata_path():
    import ctypes
    from ctypes import wintypes, windll
    CSIDL_APPDATA = 26
    _SHGetFolderPath = windll.shell32.SHGetFolderPathW
    _SHGetFolderPath.argtypes = [wintypes.HWND,
                                 ctypes.c_int,
                                 wintypes.HANDLE,
                                 wintypes.DWORD,
                                 wintypes.LPCWSTR]
    path_buf = wintypes.create_unicode_buffer(wintypes.MAX_PATH)
    result = _SHGetFolderPath(0, CSIDL_APPDATA, 0, 0, path_buf)
    return path_buf.value

def dropbox_home():
    from platform import system
    import base64
    import os.path
    _system = system()
    if _system in ('Windows', 'cli'):
        host_db_path = os.path.join(_get_appdata_path(),
                                    'Dropbox',
                                    'host.db')
    elif _system in ('Linux', 'Darwin'):
        host_db_path = os.path.expanduser('~'
                                          '/.dropbox'
                                          '/host.db')
    else:
        raise RuntimeError('Unknown system={}'
                           .format(_system))
    if not os.path.exists(host_db_path):
        raise RuntimeError("Config path={} doesn't exists"
                           .format(host_db_path))
    with open(host_db_path, 'r') as f:
        data = f.read().split()

    return base64.b64decode(data[1])
richardthe3rd

There is an answer to this on Dropbox Help Center - How can I programmatically find the Dropbox folder paths?

Short version:

Use ~/.dropbox/info.json or %APPDATA%\Dropbox\info.json

Long version:

Access the valid %APPDATA% or %LOCALAPPDATA% location this way:

import os
from pathlib import Path
import json

try:
    json_path = (Path(os.getenv('LOCALAPPDATA'))/'Dropbox'/'info.json').resolve()
except FileNotFoundError:
    json_path = (Path(os.getenv('APPDATA'))/'Dropbox'/'info.json').resolve()

with open(str(json_path)) as f:
    j = json.load(f)

personal_dbox_path = Path(j['personal']['path'])
business_dbox_path = Path(j['business']['path'])

You could search the file system using os.walk. The Dropbox folder is probably within the home directory of the user, so to save some time you could limit your search to that. Example:

import os
dropbox_folder = None

for dirname, dirnames, filenames in os.walk(os.path.expanduser('~')):
    for subdirname in dirnames:
        if(subdirname == 'Dropbox'):
            dropbox_folder = os.path.join(dirname, subdirname)
            break
    if dropbox_folder:
        break

# dropbox_folder now contains the full path to the Dropbox folder, or
# None if the folder wasn't found

Alternatively you could prompt the user for the Dropbox folder location, or make it configurable via a config file.

maoizm

Note: answer is valid for Dropbox v2.8 and higher

Windows

jq -r ".personal.path" < %APPDATA%\Dropbox\info.json

This needs jq - JSON parser utility to be installed. If you are happy user of Chocolatey package manager, just run choco install jq before.

Linux

jq -r ".personal.path" < ~/.dropbox/info.json 

Just similarly to Windows install jq using package manager of your distro.

Note: requires Dropbox >= 2.8

Dropbox now stores the paths in json format in a file called info.json. It is located in one of the two following locations:

%APPDATA%\Dropbox\info.json
%LOCALAPPDATA%\Dropbox\info.json

I can access the %APPDATA% environment variable in Python by os.environ['APPDATA'], however I check both that and os.environ['LOCALAPPDATA']. Then I convert the JSON into a dictionary and read the 'path' value under the appropriate Dropbox (business or personal).

Calling get_dropbox_location() from the code below will return the filepath of the business Dropbox, while get_dropbox_location('personal') will return the file path of the personal Dropbox.

import os
import json

def get_dropbox_location(account_type='business'):
    """
    Returns a string of the filepath of the Dropbox for this user

    :param account_type: str, 'business' or 'personal'
    """
    info_path = _get_dropbox_info_path()
    info_dict = _get_dictionary_from_path_to_json(info_path)
    return _get_dropbox_path_from_dictionary(info_dict, account_type)

def _get_dropbox_info_path():
    """
    Returns filepath of Dropbox file info.json
    """
    path = _create_dropox_info_path('APPDATA')
    if path:
        return path
    return _create_dropox_info_path('LOCALAPPDATA')

def _create_dropox_info_path(appdata_str):
    r"""
    Looks up the environment variable given by appdata_str and combines with \Dropbox\info.json

    Then checks if the info.json exists at that path, and if so returns the filepath, otherwise
    returns False
    """
    path = os.path.join(os.environ[appdata_str], r'Dropbox\info.json')
    if os.path.exists(path):
        return path
    return False

def _get_dictionary_from_path_to_json(info_path):
    """
    Loads a json file and returns as a dictionary
    """
    with open(info_path, 'r') as f:
        text = f.read()

    return json.loads(text)

def _get_dropbox_path_from_dictionary(info_dict, account_type):
    """
    Returns the 'path' value under the account_type dictionary within the main dictionary
    """
    return info_dict[account_type]['path']

This is a pure Python solution, unlike the other solution using info.json.

One option is you could go searching for the .dropbox.cache directory which (at least on Mac and Linux) is a hidden folder in the Dropbox directory.

I am fairly certain that Dropbox stores its preferences in an encrypted .dbx container, so extracting it using the same method that Dropbox uses is not trivial.

martineau

This should work on Win7. The use of getEnvironmentVariable("APPDATA") instead of os.getenv('APPDATA') supports Unicode filepaths -- see question titled Problems with umlauts in python appdata environvent variable.

import base64
import ctypes
import os

def getEnvironmentVariable(name):
    """ read windows native unicode environment variables """
    # (could just use os.environ dict in Python 3)
    name = unicode(name) # make sure string argument is unicode
    n = ctypes.windll.kernel32.GetEnvironmentVariableW(name, None, 0)
    if not n:
        return None
    else:
        buf = ctypes.create_unicode_buffer(u'\0'*n)
        ctypes.windll.kernel32.GetEnvironmentVariableW(name, buf, n)
        return buf.value

def getDropboxRoot():
    # find the path for Dropbox's root watch folder from its sqlite host.db database.
    # Dropbox stores its databases under the currently logged in user's %APPDATA% path.
    # If you have installed multiple instances of dropbox under the same login this only finds the 1st one.
    # Dropbox stores its databases under the currently logged in user's %APPDATA% path.
    # usually "C:\Documents and Settings\<login_account>\Application Data"
    sConfigFile = os.path.join(getEnvironmentVariable("APPDATA"),
                               'Dropbox', 'host.db')

    # return null string if can't find or work database file.
    if not os.path.exists(sConfigFile):
        return None

    # Dropbox Watch Folder Location is base64 encoded as the last line of the host.db file.
    with open(sConfigFile) as dbxfile:
        for sLine in dbxfile:
            pass

    # decode last line, path to dropbox watch folder with no trailing slash.
    return base64.b64decode(sLine)

if __name__ == '__main__':
    print getDropboxRoot()
zkurtz

This adaptation based on J.F. Sebastian's suggestion works for me on Ubuntu:

os.path.expanduser('~/Dropbox')

And to actually set the working directory to be there:

os.chdir(os.path.expanduser('~/Dropbox'))
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!