65.9K
CodeProject 正在变化。 阅读更多。
Home

GDrive Explorer - 用于 Google Drive

starIconstarIconstarIconstarIcon
emptyStarIcon
starIcon

4.88/5 (62投票s)

2013年9月3日

CPOL

8分钟阅读

viewsIcon

170828

downloadIcon

16096

浏览您的 Google Drive,并将您的 Windows Explorer 与 Google 的存储云集成。

引言

云存储巧妙地成为许多解决方案的一个良好卖点。尽管“云”这个术语有点言过其实(纯属个人观点),但它解决了大部分遗留问题。至少有一群人感到安心,因为管理(无论是存储还是其他任何事情)的复杂性以可承受的成本交给了最优秀的人才。例如,维护冗余存储介质的明显缺点不再需要处理。因此,Google Drive 和 Sky Drive 等业务在如此短的时间内,从消费者到中小企业再到大型企业解决方案,都取得了巨大的发展。

为了使事情更灵活或更专业,现在我们可以从这些提供商那里获得免费的开发 API。这些 API 不仅使私有应用程序能够集成,还为最终用户提供了只需点击一下鼠标即可无缝使用云服务的可能性。

这让我想到最终用户。很多时候,这些对云或雨如此着迷的“机器人”急需一种既能防呆又“面向云”的解决方案(我特意在一个比实际含义更小的范围内使用“面向云”这个术语)。我个人在不同的会议上(有些与云计算相关)与一群人交谈过,他们理解这种需求,并且确实需要“一键式按需云存储”,这种存储可以与他们遗留的(或不那么遗留的)企业存储解决方案集成。其中一些人对企业云很满意,另一些人则对他们的私有云很满意。但最有趣的是,他们正在讨论已经可用的云解决方案。简而言之,他们希望用 Google Drive、Sky Drive、Box 或 Dropbox 等云服务无缝替换他们现有的存储框架。他们谈论的原因也相当合乎逻辑。他们不想每月购买硬盘。他们也不想通过不维护自动复制的计划脚本来冒数据丢失的风险。更简单地说,他们想把那些令人头疼的事情留给那些已经为之头疼的人 :)

图 0:主浏览器用户界面 

背景

正是在一次“每天通勤办公室 6 小时”的经历中,我遇到了一位数据丢失恐惧症患者。他也提出了同样的要求,这让我决定使用一个免费的公共存储 API 进行概念验证。在尝试了几个之后,我发现 Google 的 API 比其他 API 更“流畅”,于是决定着手进行。

经过几次初步尝试,我很好地掌握了仍在成熟的 Google Drive 2 API。凭借过去在公开存储数据服务器 API 方面的经验,我很好奇它们之间有多么相似,以及客户端应用程序期望有多么舒适 :) 概念验证很快演变成一个适用于 Windows-7 桌面电脑的半漂亮的存储盒。Drive2 的 C# SDK 是最大的赌注,加上一些演示和队列实现,我可以在一周内捆绑这个小程序。虽然离完美还有很远,还需要更多改进,但我决定撰写我的文章,以供评论并分享一些功能代码,供那些仍在决定使用 Google Drive2 API SDK 的人参考。

使用应用程序

嘿,这就是“我的 GDrive Explorer”或“GDrive Explorer”(这两个分别是发布版本和开发版本名称)。

用户可以使用该应用程序做什么? 

  • 通过将用户引导至“授予访问权限”URL 来请求授权码
  • 获得访问权限后,左侧面板会显示一些用户信息
  • 再次获得访问权限后,右侧面板会显示驱动器内容
  • 允许授权用户像 Windows 资源管理器一样浏览您的驱动器(返回/前进/主页/刷新)
  • 允许授权用户创建新文件夹
  • 允许授权用户上传/下载文件
  • 允许授权用户删除/重命名文件/文件夹
  • 目前支持 Word/Excel/PowerPoint/文本/PDF 文件上传(Google Drive 支持以下类型的文件 https://support.google.com/drive/answer/2423485?hl=en
  • 支持所有用户文件的下载(Google 不允许用户下载使用 Google Docs 创建的文件)
  • 并行上传多个文件(带上传进度通知)
  • 并行下载多个文件(带下载进度通知)
  • 在当前驱动器文件夹中搜索文件/文件夹(自由文本搜索)
  • 将文件(非文件夹)扔进回收站
  • 回收站视图,能够恢复/永久删除已删除的文档
  • 通过右键单击文件项直接从资源管理器将文档发送到 GoogleDrive(仅支持单选。上下文菜单项在 Windows 7 上有效,并且在应用程序运行期间可见)
  • 通过安装额外的 Office COM 插件(使用 NetOffice - MS Office in .NET 开发 - http://netoffice.codeplex.com/)直接从 MS Office 应用程序(Word、Excel、PowerPoint 和 Outlook)发送文档

使用提示 

该应用程序使用 oauth2 身份验证机制,并会提示用户授予文件和关于机制的访问权限。用户必须点击“接受”按钮以响应这两个请求。页面会显示在一个弹出式网络浏览器控件中。成功后,响应消息会被抓取,并且授权令牌用于访问。如果抓取失败,则会显示一个备用授权令牌对话框以进行手动身份验证。

这里会有两个对话框,一个用于“关于”,一个用于“文件”。两者都必须被允许才能进一步访问。

我的经验是,用户必须在这两个授权对话框上点击两三次才能激活它。这是一个已知问题,正在解决中。

图 1:授权对话框 

 

图 2:授权回退对话框 

如果失败,应用程序将不允许继续并会关闭。

安全问题:本文提供了完整的代码,应用程序不会对您的账户做任何恶意事情。您可以选择仔细查看整个代码库以确保您的安全。

成功登录后,用户应该看到如下可探索的屏幕。这些功能都有工具提示并自解释。有关详细的使用指南,请下载本文随附的用户手册。

图 3:上传/下载进度指示 

当应用程序处于活动状态时,用户可以将选定的文件直接发送到应用程序。这些文件将直接排队等待上传到 Google Drive 中当前选定的文件夹。

图 4:与 Windows 资源管理器集成 

安装 MS Office 插件 

下载本文顶部附件中的“MicrosoftOfficeAddIns.zip”。这些插件是适用于 Office 2007 和 2010 的 Office COM 插件,使用 NetOffice - MS Office in .NET (http://netoffice.codeplex.com/) 开发。

下载后,您可以使用命令行中的标准“regasm”命令进行安装或卸载。这些命令已随下载文件一起附在“bat”文件中。

安装使用

regasm GDrive.Explorer.MSOfficeAddIn.dll

卸载使用:

regasm GDrive.Explorer.MSOfficeAddIn.dll /unregister

如果一切顺利,重启 Office 应用程序后您应该会看到以下内容。一旦您点击“发送”按钮,文档将直接上传到您的 Google Drive 云存储空间。

请注意,发送到 Google Drive **仅在** GDrive Explorer 应用程序处于活动并运行状态时才有效。

源代码可以通过包含来自 http://netoffice.codeplex.com/ 的必要二进制文件进行编译。

它是如何工作的?

GDrive.Framework

对代码稍作深入研究。Google Drive API 的代码被封装在一个名为 GDrive.Framework 的框架中。该框架充当存储 API 访问的门面。

有一个通过 oAuth 2 进行的身份验证机制,并且必须通过授予访问权限机制接收授权码。这通过 GDriveAuthentication 类中的 Web 浏览器控件进行路由。代码与这里提到的相同 [https://developers.google.com/drive/quickstart-cs]

附件中的代码依赖于这些二进制文件,并且只有在引用它们时才能编译。

其他链接 

Drive2 API 

Google API 

public class GDriveAuthentication
{
     public DriveService GetDriveService()
     {
        string CLIENT_ID = "CLIENT ID";
        string CLIENT_SECRET = "CLIENT SECRET";

        // Register the authenticator and create the service
        var provider = new NativeApplicationClient(
            GoogleAuthenticationServer.Description, CLIENT_ID, CLIENT_SECRET);
        var auth = new OAuth2Authenticator<NativeApplicationClient>(provider, GetAuthorization);
        var service = new DriveService(new BaseClientService.Initializer()
        {
            Authenticator = auth
        });

        return service;
    }

    private IAuthorizationState GetAuthorization(NativeApplicationClient arg)
    {
        // Get the auth URL:
        IAuthorizationState state = 
          new AuthorizationState(new[] { DriveService.Scopes.Drive.GetStringValue() });
        state.Callback = new Uri(NativeApplicationClient.OutOfBandCallbackUrl);
        Uri authUri = arg.RequestUserAuthorization(state);

        // Request authorization from the user (by opening a browser window):

        //Process.Start(authUri.ToString());
        //Console.Write("  Authorization Code: ");
        //string authCode = Console.ReadLine();
        //Console.WriteLine();

        var authCode = AuthorizationWindow.GetToken(authUri.ToString());

        if (string.IsNullOrEmpty(authCode))
            authCode = Interaction.InputBox("We did not find authcode. " + 
              "Please enter here to continue", "Authorization Code", string.Empty);

        // Retrieve the access token by using the authorization code:
        return arg.ProcessUserAuthorization(authCode ?? string.Empty, state);
    }
}

用于探索驱动器的主门面类(称为 GDriveExplorer)提供了常用方法的通用实现。

public class GDriveExplorer
{
    private DriveService service;

    public GDriveExplorer()
    {
        service = new GDriveAuthentication().GetDriveService();
    }

    public GDriveFilesResponse GetFiles(string parentId = "root", string search = null,
        string pageToken = null, int? perpage = null, SortOrder order = SortOrder.LastModifiedDesc)
    {
       ...
    }

    public GDriveFilesResponse GetTrashedFiles(string search = null, 
              string pageToken = null, int? perpage = null)
    {
       ...
    }

    public bool TrashFile(string id)
    {
        ...
    }

    public bool UnTrashFile(string id)
    {
       ...
    }

    public bool DeleteFile(string id)
    {
        ...
    }

    public GDriveAbout GetInfo()
    {
       ...
    }

    public object UploadFileAsync(UploadQueueItem item, ProgressChange progressCallback)
    {
        ...
    }

    public bool UploadFile(UploadQueueItem item)
    {
        ...
    }

    public GDriveFile CreateFolder(GDriveFile folder, string parent)
    {
        ...
    }

    public GDriveFile Update(GDriveFile file)
    {
        ...
    }

    public object DownloadFile(DownloadQueueItem item, ProgressChange progressCallback)
    {
        ...              
    }
}

GDrive.Explorer

这是一个使用 MVVM 的 WPF 应用程序。整个代码按照 MVVM 实践进行设计,因此代码隐藏中绝对没有代码。最严格的 MVVM 很难实现,有时也不切实际。所以许多人将其作为指导而非实践。我个人对 MVVM 的经验也很初级,要称之为实践,我还需要更努力。然而,这只是初步实践,因此可以没有代码隐藏。这就是代码隐藏包含的内容 :)

public partial class MainWindow : Window
{
    public MainWindow()
    {
	InitializeComponent();
    }
}

主应用程序是一个**单例应用程序**。这意味着您不能启动超过一个应用程序实例。实现此功能的类是 App,它使用 Microsoft.VisualBasic 遵循标准单例实例应用程序。

public class SingleInstanceManager : WindowsFormsApplicationBase
{
    private App application;
    private ReadOnlyCollection<string> commandLine;

    public SingleInstanceManager()
    {
        IsSingleInstance = true;
    }

    protected override bool OnStartup(Microsoft.VisualBasic.ApplicationServices.StartupEventArgs eventArgs)
    {
        // First time _application is launched
        commandLine = eventArgs.CommandLine;
        application = new App();
        application.Run();           

        return false;
    }

    protected override void OnShutdown()
    {
        base.OnShutdown();
    }

    protected override void OnStartupNextInstance(StartupNextInstanceEventArgs eventArgs)
    {
        // Subsequent launches
        base.OnStartupNextInstance(eventArgs);
        //commandLine = eventArgs.CommandLine;
        application.Activate(eventArgs.CommandLine);
    }
}

上传和下载队列实现用于实现带有进度更新器的异步上传/下载模式。不知何故,上传通知只在上传结束时触发。我仍在研究 AsyncUpload 函数。下载功能会提供进度通知。这些类可以在附件代码库中找到。

任务栏中的通知图标用于控制将应用程序发送到后台。可以双击该图标以显示/隐藏应用程序窗口而不关闭它。

public NotifyIconManager(Window mainwindow)
{
    try
    {
       System.Windows.Forms.NotifyIcon ni = new System.Windows.Forms.NotifyIcon()
       {
           Icon = System.Drawing.Icon.ExtractAssociatedIcon(this.GetType().Assembly.Location),
           ContextMenu = new System.Windows.Forms.ContextMenu(),
           Text = "Doubleclick to Show/Hide Window",
            Visible = true
       };

       ni.ContextMenu.MenuItems.Add(new System.Windows.Forms.MenuItem(
                           "Exit", (o, e) => App.InvokeShutDown()));
       ni.DoubleClick += (s, args) =>
       {
           if (!mainwindow.IsVisible)
           {
               mainwindow.Show();
               mainwindow.Activate();
           }
           else
           {
             mainwindow.Hide();
           }
       };
    }
    catch { }
}

Windows 资源管理器中带有图标的上下文菜单项可以通过 RegistryEntry 类中的注册表修改来实现。

public class RegistryEntry
{
    public void CreateRegistry(params string[] extensions)
    {
        var filename = Assembly.GetEntryAssembly().Location;

        foreach (var ext in extensions)
        {
            var regmenu = Registry.CurrentUser.CreateSubKey("SOFTWARE\\Classes\\" + ext + "\\shell\\MyGDrive");
            if (regmenu != null)
            {
                regmenu.SetValue("", "Send to My GDrive");
                regmenu.SetValue("icon", "\"" + filename + "\",0");  // icon location - works in win7 and later
                regmenu.SetValue("MultiSelectModel", "Single"); // one by one entry mode - works in win7 and later
                regmenu.SetValue("EditFlags", new byte[] { 0x01, 0x00, 0x00, 0x00 }, RegistryValueKind.Binary);

                var regcmd = regmenu.CreateSubKey("command");
                if (regcmd != null)
                    regcmd.SetValue("", "\"" + filename + "\" \"%1\"");
            }
        }
    }

    public void RemoveRegistry(params string[] extensions)
    {
        foreach (var ext in extensions)
        {
            var key = "SOFTWARE\\Classes\\" + ext + "\\shell\\MyGDrive";
            if (Registry.CurrentUser.OpenSubKey(key) != null)
            {
                Registry.CurrentUser.DeleteSubKeyTree(key);
            }
        }
    }

    public bool IsUserAdministrator
    {
        get
        {
            bool isAdmin = false;
            try
            {
                var principal = new WindowsPrincipal(WindowsIdentity.GetCurrent());
                isAdmin = principal.IsInRole(WindowsBuiltInRole.Administrator);
            }
            catch (UnauthorizedAccessException)
            {
            }
            catch (Exception)
            {
            }
            return isAdmin;
        }
    }
}

演示层的其余部分分为以下几个部分

  • 命令:视图模型中使用的 RelayCommands
  • 控件:应用程序中使用的单个控件是 SearchTextBox
  • 转换器:许多值绑定使用这些标准转换器进行演示
  • 视图模型:视图中使用的视图模型
  • 图片:视图中使用的图片/图标资源

使用代码 

GDrive.Framework 可以如下使用。

///Getting list of files in a folder

GDriveFilesResponse filesResponse = GetFiles(parentId, searchString, pageToken, perpage, sortorder);
string nextpagetoken = filesResponse.NextPageToken;
filesResponse.Files.ToList().ForEach(f =>
{
  // parse file information
});

/// Getting Trashed files in a folder
GDriveFilesResponse filesresponse = GDriveFilesResponse GetTrashedFiles(searchstring, pageToken, perpage);

/// send a file to trash
bool success =  TrashFile(fileid);

/// restore a file from trash
bool success =  UnTrashFile(fileid);

/// permanently delete a file
bool success = DeleteFile(fileid);

/// getting about info
GDriveAbout aboutinfo = GetInfo();

///upload file async
object result = UploadFileAsync(uploadQueueItem, progressChangeHandler);

///upload file syncronous
bool success = UploadFile(uploadQueueItem);

///creating a folder
GDriveFile folderinfo = CreateFolder(gDriveFileInfo, parentfolderid);

///update filename
GDriveFile fileinfo = Update(gDriveFileInfo);

///download file
object result = DownloadFile(downloadQueueItem, progressChangeHandler);

已知问题 

  1. Google 尚不支持按名称等排序,这些区域已报告变更请求(目前默认为按修改日期降序排序)。
  2. 我尚未测试异步上传。现有实现是使用 BeginInvoke 中的同步上传功能。
  3. 分页尚未实现(目前只显示前 100 项)。我正在寻找一种在遵循 MVVM(无代码隐藏)的情况下实现虚拟滚动的方法——欢迎在此领域提出建议。
  4. 其他一些新功能正在开发中。

历史

  • 首次发布日期 - 2013 年 9 月 3 日
  • 添加 MS Office 插件 - 2013 年 9 月 6 日
  • 构建修复 - 2013 年 9 月 16 日
© . All rights reserved.