Build directory tree from dropbox API

2019-02-26 04:40发布

问题:

What I'd like to do is to build a tree from the dropbox API, for a given path, with share links for each path, using the python bindings.

My proposed structure looks something like this:

[
    {
        'path': '/a',
        'is_dir': True,
        'contents': [
            {
                'path': '/a/b',
                'is_dir': True,
                'contents': [etc]
            },
            {
                'path': '/a/readme.txt',
                'is_dir': False,
                'share_link': 'http://etc'
            }
        ]
    },
    etc.
]

I've got something that mostly works using metadata() but it's hideously slow as it needs to make an API call per directory traversed.

What I'd like to use instead is delta(), which will get me every file in one request, then build it into a tree, but I'm having problems figuring out exactly how, in particular how to parse the paths into a tree.

Edit: And I've realised there's a call for each share link so I'm going to omit those and just get them when requested.

Here's some code I have to get the data I need so far:

paths = []

for path, metadata in client.delta(path_prefix='/whatever')['entries']:
    paths.append({
        'path': path,
        'is_dir': metadata['is_dir']
    })

So I guess I'm having trouble figuring out how to get those paths nested. Pretty sure I need a recursive function here but can't quite figure it out.

回答1:

I tweaked your structure just a bit... here's the JSON representation of what the below code produces. Note that I've made the contents field a dictionary indexed by path instead of an array. This is just a little easier and makes for more efficient lookup, but it should be pretty easy to convert to exactly what you have above if you want:

{
    "is_dir": true,
    "contents": {
        "/foo.txt": {
            "is_dir": false,
            "contents": {}
        },
        "/a": {
            "is_dir": true,
            "contents": {
                "/a/bar.txt": {
                    "is_dir": false,
                    "contents": {}
                },
                "/a/b": {
                    "is_dir": true,
                    "contents": {
                        "/a/b/hello.txt": {
                            "is_dir": false,
                            "contents": {}
                        }
                    }
                }
            }
        }
    }
}

Here's the code that produces that output:

ACCESS_TOKEN = '<REDACTED>'

from collections import defaultdict
import json

from dropbox.client import DropboxClient

def make_tree():
    return {
        'is_dir': True,
        'contents': defaultdict(make_tree)
    }
tree = defaultdict(make_tree)

client = DropboxClient(ACCESS_TOKEN)

has_more = True
cursor = None

while has_more:
    delta = client.delta(cursor)

    cursor = delta['cursor']
    has_more = delta['has_more']

    for path, metadata in delta['entries']:
        if metadata is not None:

            # find the right place in the tree
            segments = path.split('/')
            location = tree['/']
            for i in xrange(1, len(segments)-1):
                current_path = '/'.join(segments[:i+1])
                location = location['contents'][current_path]

            # insert the new entry
            location['contents'][path] = {
                'is_dir': metadata['is_dir'],
                'contents': {}
            }

print json.dumps(tree['/'], indent=4)