使用 Javascript 在 Google 驱动器中创建树状目录结构

Create a tree directory structure in Google Drive with Javascript

我正在尝试使用 Google 驱动器 Javascript API 构建一个简单的目录树。我想我应该检查文件夹是否存在并创建它,然后最终添加它的子文件夹。所以我在下面构建了这些函数:

function createFolder(name, parent) {
    if (!parent) parent = 'root';
    var fileMetadata = {
        'name': name,
        'mimeType': 'application/vnd.google-apps.folder',
        'parent': parent
    };
    gapi.client.drive.files.create({
        'resource': fileMetadata,
        'fields': 'id'
    }).then(function (res) {console.log(res); return res.result['id'];}, function (err) {console.log(err); return null;});
}

function getFolder(name, parent) {
    if (!parent) parent = 'root';
    gapi.client.drive.files.list({
        'pageSize': 1,
        'fields': "nextPageToken, files(id, name, mimeType, parents)",
        'q': "mimeType='application/vnd.google-apps.folder' \
                    and '" + parent + "' in parents \
                    and name = '" + name + "'"
    }).then(
        function (res) {console.log(res); var ret = res.result.files.length > 0 ? res.result.files[0]['id'] : null; return ret;},
        function (err) {console.log(err);return null;}
    );
}

function checkPhrFolder() {
    var phr = getFolder('Personal Health Record');
    console.log('get folder: '+phr);
    if (!phr) {
        console.log('creating ...');
        phr = createFolder('Personal Health Record');
    }
}

这只是检查我的第一个目录是否存在,如果不存在,则创建它。问题是调用是异步的(使用 "then promises")所以函数不会 return 任何东西(例如 getFolder 从不 returns 文件夹的 id),所以我想知道什么鉴于异步调用的性质,是递归创建多个文件夹的正确方法。我是否需要放置一个始终被调用并选择下一步做什么的控制器函数?

谢谢!

在这种情况下,我发现新的异步 javascript 语法更容易理解。

方法

你是 运行 函数 checkPhrFolder 同步,而其他两个依赖于异步 api 调用的函数正在等待 promise 解析以便 return一个值。当您在同步 checkPhrFolder 执行中评估它时,这将导致 phr 未定义。

基本上,要使其正常工作,您应该链接您的承诺解决方案,以便正确使用它们的 return 值。新的 javascript 语法使您能够用更少的代码行编写异步函数。 使用关键字 asyncawait,您可以控制承诺解析流程,并且基本上等待异步函数到 return,然后再进行适当的分配。

例子

async function createFolder(name, parent) {
    if (!parent) parent = 'root';
    var fileMetadata = {
        'name': name,
        'mimeType': 'application/vnd.google-apps.folder',
        'parent': parent
    };
    const res = await gapi.client.drive.files.create({
        'resource': fileMetadata,
        'fields': 'id'
    });
    return res.result['id'];
}

async function getFolder(name, parent) {
    if (!parent) parent = 'root';
    const res = await gapi.client.drive.files.list({
        'pageSize': 1,
        'fields': "nextPageToken, files(id, name, mimeType, parents)",
        'q': "mimeType='application/vnd.google-apps.folder' \
                    and '" + parent + "' in parents \
                    and name = '" + name + "'"
    });
    return res.result.files.length > 0 ? res.result.files[0]['id'] : null;
}

async function checkPhrFolder() {
  var phr = await getFolder('Personal Health Record');
  console.log('get folder: '+phr);
  if (!phr) {
      console.log('creating ...');
      phr = createFolder('Personal Health Record');
  }
}

这样您的 checPhrFolder 函数将几乎和以前一样容易阅读。当您将在同步上下文中使用 checkPhrFolder 函数时,您将能够将其 return 值包装在同一个 then/catch 语句中。

参考

Async/Await

我使用传统方式构建我的 dirs populator,如下所示:

    var phrFolderManager = {
        currentName: null,
        currentParent: null,
        folders: {
            '/PersonalHealthRecord': null,
            '/PersonalHealthRecord/Some': null,
            '/PersonalHealthRecord/Dirs': null,
            '/PersonalHealthRecord/ForMe': null
        },
        addFolder: function (name, id) {
            this.folders[name] = id;
        },
        getFolderId: function (name) {
            return this.folders[name];
        },
        refresh: function (forced) {
            console.log('next step ' + forced + ' ...');
            console.log(this.folders);
            // find the next null in our folder list
            for (k in this.folders) {
                this.currentName = k;
                if (!this.folders[k]) {
                    var parts = k.split('/');
                    if (parts.length == 2) {
                        // this is our base dir inside root
                        if (forced) {
                            this.createFolder(parts[1], 'root');
                        } else {
                            this.getFolder(parts[1], 'root');
                        }
                    } else {
                        var parent = parts.slice(0, -1).join('/');
                        var name = parts[parts.length - 1];
                        var parent_id = this.folders[parent];
                        if (forced) {
                            this.createFolder(name, parent_id);
                        } else {
                            this.getFolder(name, parent_id);
                        }
                    }
                    break;
                } else {
                    console.log('... defined as ' + this.folders[k]);
                }
            }
        },
        getFolder: function (name, parent) {
            //M.toast({html: 'check da pasta '+name,classes: 'rounded'});
            if (!parent) parent = 'root';
            this.currentParent = parent;
            console.log('getFolder ' + name + ' ' + parent);
            var res = gapi.client.drive.files.list({
                'pageSize': 1,
                'fields': "files(id, name, mimeType, parents)",
                'q': "mimeType='application/vnd.google-apps.folder' \
                        and '" + parent + "' in parents \
                        and name = '" + name + "'"
            }).then(
                function (res) {
                    console.log(res);
                    if (res.result.files.length > 0) {
                        this.folders[this.currentName] = res.result.files[0]['id'];
                        this.refresh(false);
                    } else {
                        this.refresh(true);
                    }
                },
                function (err) {
                    console.log('error in getFolder: ' + err)
                },
                this
            );
        },
        createFolder: function (name, parent) {
            M.toast({
                html: 'criando pasta ' + name,
                classes: 'rounded'
            });
            if (!parent) parent = 'root';
            console.log('createFolder ' + name + ' ' + parent);
            var fileMetadata = {
                'name': name,
                'mimeType': 'application/vnd.google-apps.folder',
                'parents': [parent]
            };
            gapi.client.drive.files.create({
                'resource': fileMetadata,
                'fields': 'id'
            }).then(
                function (res) {
                    console.log(res);
                    this.folders[this.currentName] = res.result['id'];
                    this.refresh();
                },
                function (err) {
                    alert('Problem creating ' + this.currentName + ' PersonalHealthRecord structure');
                },
                this
            );
        }
    };

稍微调整一下,@Alessandro 的解决方案对我有用:

  • 为了便于阅读,我颠倒了方法的顺序。
  • 我合并了一个 FOR 循环,因为我的文件结构将深入许多文件夹。循环通过包含文件路径中的文件夹名称的字符串数组。
  • 我注意到 (!parent) 没有正确返回,因为它是“未定义”,而不是“空”。此外,当找不到现有文件夹时期待“n​​ull”是至关重要的,所以我将 parentId 默认为“root”,因为我知道我的第一个文件夹将始终位于根目录
  • 变量 newFolderId 保存 found/created 文件夹的值,并传递给 parentId 用于 FOR 循环的下一次迭代
  • 最后,我将“'parent': parent”更新为“'parents' : [parent]”。那个“s”和那些括号似乎是必要的,否则每个创建的文件夹都放在根目录下。
        async function buildFilePath() {

            console.log("buildFilePath()");

            var parentId = "root";

            for (let i = 0; i < filePath.length; i++)  {
                var folderName = filePath[i];

                newFolderId = await getFolder(folderName, parentId);
                console.log(folderName + " id is " + newFolderId + ", parentId is " + parentId);
                if (newFolderId == null) {
                    newFolderId = await createFolder(folderName,parentId);
                    console.log ("created new folder " + folderName + " with Id " + parentId);
                } else {
                    // parentId = newParentId;
                    console.log (folderName + " already exist, move on to next.");
                }
                parentId = newFolderId;
            }
        }
        async function getFolder(name, parent) {

            const res = await gapi.client.drive.files.list({
                'pageSize': 1,
                'fields': "nextPageToken, files(id, name, mimeType, parents)",
                'q': "mimeType='application/vnd.google-apps.folder' \
                            and '" + parent + "' in parents \
                            and name = '" + name + "' \
                            and trashed = false"
            });
            return res.result.files.length > 0 ? res.result.files[0]['id'] : null;
        }
        async function createFolder(name, parent) {

            if (!parent) parent = 'root';

            var fileMetadata = {
                'name': name,
                'mimeType': 'application/vnd.google-apps.folder',
                'parents': [parent]
            };
            const res = await gapi.client.drive.files.create({
                'resource': fileMetadata,
                'fields': 'id'
            });
            return res.result['id'];
        }