在 WPF 中使用 Google Drive






4.96/5 (19投票s)
对 Google Drive 进行身份验证和上传文件。
引言
在设计我参加 App Innovation 竞赛(如果您有兴趣,该竞赛名为 Rev Warrior,可以在此处找到)时,我想要的一个功能是能够将程序记录的数据存储到用户的 Google Drive 中。目的是使数据易于访问,以便稍后在 Excel 或其他程序中进行分析。
我发现的是,除了 Google 提供的一个非常简单的示例之外,在 .NET 中没有关于如何做到这一点的文档或工作示例。尽管他们的示例可行,但每次运行时都需要显式授权,而且它并没有真正解决示例如何以及为何有效的问题。所以,我开始深入研究这个问题,当我找到解决方案时,我觉得我应该在这里分享。附带的源代码是一个非常简单的 WPF 应用程序,运行时会打开一个带有单个按钮的窗口。点击按钮,将弹出一个文件选择对话框,提示您选择要上传的文件。选择一个文件,应用程序将尝试与 Google Drive 进行身份验证。如果需要,它会打开一个浏览器,以便您可以授权访问,并且只要您同意,程序就会将您选择的文件上传到您的 Google Drive。
我的应用程序与其他示例的不同之处在于,我的应用程序扩展了 Google 的示例代码,以存储授权令牌并在后续访问中使用它们,这样用户就不会每次运行程序时都被要求授权。其结果是流程相当简洁,第一次运行时会提示用户,但之后会默默上传所选文件,前提是应用程序保持授权状态。(Google 用户可以在其用户控制面板中撤销对其 Google 服务的授权。)
实际上传非常简单,因为 Google 提供了所有供您使用的库。复杂之处在于授权序列以及如何缓存这些令牌并在后续请求中使用它们。Google 的文档读起来像立体声说明,而且没有太多可供参考的示例。Google 提供了一个名为 Dr. Edit 的完整应用程序作为 .NET 示例,但它是一个 ASP MVC 应用程序,对于我们这些使用 WPF、WinForms 或其他类似工具开发的人来说并没有多大帮助。为了获得一个可行的 WPF 解决方案,我不得不逆向工程了几个 Google 的示例项目。好处是授权步骤相当通用,可以应用于几乎所有 Google 的 API 服务,包括 Tasks、Calendar、Maps 等,因此您可以将此项目作为访问许多 Google API 服务的基石。
背景
许多 Google 服务需要两种类型的身份验证;用户必须经过身份验证(登录),并且应用程序必须获得用户的明确授权。(有关 Google Drive 授权信息,请参阅Google Drive 授权 API 文档。)
授权机制基于 OAuth 2.0。总的来说,Google 的 OAuth 机制要求请求访问的应用程序将客户端 ID(标识应用程序对 Google 的身份)和客户端密钥与应用程序希望访问的范围一起提供给授权服务终结点。(*注意:授权范围,包含在下面的 SCOPES 数组中,每个服务都不同。请参阅您希望使用的 Google API 文档的授权部分。*)授权服务将检查应用程序是否被授权使用请求的范围,如果授权,它将返回一个**访问令牌**,该令牌将与所有服务调用一起传递。所有这些都以 JSON 格式完成,并在 Google 的 API 文档和 OAuth2.0 文档中都有明确定义。诀窍在于获取访问令牌,因为该过程的部分内容没有得到很好的记录……至少对于 .NET 开发人员来说是这样。
对于已安装的应用程序,两个必需的密钥都保留在代码中(请参阅下面的 ClientCredentials
类),并且不被视为真正的“秘密”,因为代码很容易被反编译,或者可以使用 Fiddler 等代理工具拦截 JSON。因此,在生成应用程序密钥时,Google 必须被告知它是一个“已安装的应用程序”,这样它才会以更高的警惕级别对待通信。
internal static class ClientCredentials
{
/// <summary>
/// The OAuth2.0 Client ID of your project.
/// </summary>
public static readonly string CLIENT_ID = "<< APPLICATION CLIENT ID GOES HERE >>";
/// <summary>
/// The OAuth2.0 Client secret of your project.
/// </summary>
public static readonly string CLIENT_SECRET = "<< APPLICATION SECRET GOES HERE >>";
/// <summary>
/// The OAuth2.0 scopes required by your project.
/// </summary>
public static readonly string[] SCOPES = new String[]
{
"https://www.googleapis.com/auth/drive.file",
"https://www.googleapis.com/auth/userinfo.email",
"https://www.googleapis.com/auth/userinfo.profile"
};
/// <summary>
/// The Redirect URI of your project.
/// </summary>
public static readonly string REDIRECT_URI = "urn:ietf:wg:oauth:2.0:oob";
}
当一个在 Google 中注册的应用程序首次尝试访问(或未缓存凭据)时,Google 将检查应用程序标识符是否被授权访问用户请求的服务。如果应用程序尚未获得授权,Google 通常会以 401 未授权异常的形式拒绝该请求。客户端实际上需要知道它是否之前已被授权。应用程序必须通过 API 控制台获得对特定服务的访问权限。稍后将详细介绍...
这一切的关键是**授权码**。如果应用程序拥有该代码,则可以假定它之前已被授权。授权码就像您在车辆管理办公室排队时获得的编号凭证……它不能让您得到服务,只能让您获得被服务的权利。授权码表示用户已授权应用程序访问服务,并通过上面看到的 ClientCredentials
类中指定的 REDIRECT_URI
返回给客户端。上面看到的神秘字符串告诉授权服务器在用户授权应用程序后返回的页面的标题中返回代码。
当应用程序想要访问 Google 服务时,它应该检查它是否拥有缓存的凭据(假设您想在首次之后跳过授权步骤)。如果没有,它应该弹出一个浏览器窗口,请求授权 Google 服务。NativeClientApplication
对象知道如何生成授权 URL,因此我们无需自行解决。
示例项目包含完成此操作所需的一切。项目中有一个 Forms 文件夹,其中包含所需的表单。它们依赖于内置浏览器,因此无需任何修改即可正常工作。
上面在浏览器控件中看到的页面是由 Google 授权服务生成的。页面将列出您的应用程序所需的权限,用户可以展开这些权限以更清楚地了解每项权限的含义。不用说,您的应用程序应该只请求完成其任务所需的最少权限。用户在授权应用程序之前应该查看这些权限……无论是您的应用程序还是任何其他应用程序。
如果用户允许访问,授权服务器将返回**授权码**。它可以由 Google 服务器调用您的应用程序打开的 Web 服务终结点返回,也可以在返回浏览器的页面的标题中返回。由于启动 Web 服务器、通过任何可能的防火墙打开连接等方面的挑战……对于已安装的应用程序来说,从结果网页的标题中抓取授权码要容易得多。这就是示例项目中使用的方法。如果成功,标题将设置为**Success code=xxxxxxxxx**,其中 xxxx 被唯一的授权码替换。
如前所述,授权码只能让您进入派对。您无法使用该代码进行 API 访问。授权码必须兑换成一个短暂的访问令牌和一个长期的刷新令牌。在 Google.Apis.Authentication.OAuth2 库中,有一个名为 NativeApplicationClient
的类。这是授权服务器的包装器,它有一个名为 'ProcessUserAuthorization
' 的方法。此方法接收用户授权应用程序访问后检索到的授权码,并将其转换为访问令牌和刷新令牌。访问令牌是我们当前任务实际需要的,它保存在 NativeApplicationClient
中。它与所有后续 API 调用一起传递。NativeApplicationClient
的好处是它知道如何验证访问令牌及其有效期。如果令牌已过期,客户端将使用刷新令牌获取新的访问令牌。这消除了我们管理令牌生命周期的负担。
缓存的凭据
前面描述的工作流程解决了应用程序需要授权才能访问 API 服务的情况。可以*始终*提示授权,并且一些关于这些类型操作的指导建议,作为一名优秀开发者,我们应该这样做。但是用户体验问题会发挥作用,我们不想每次程序运行时都需要访问他们的 Google Drive 时都用授权对话框来打扰他们。用户可能不会在浏览器中保留缓存的登录信息,不仅会被提示授权应用程序,还可能首先被提示登录,从而每次都要让他们经过两个屏幕来授权访问。这对于普通用户来说将是极大的劝退,因此我们需要更好的方法。一旦同意授权,我们就可以安全地假定用户知道他们已经授权了我们的应用程序访问他们的 Google Drive,因此我们可以合理地跳过授权步骤。在实践中,我们通过缓存的凭据来做到这一点。
在示例项目中,有一个名为 AuthorizationMgr
的辅助类,它处理(除其他事项外)授权缓存。当我们获得初始授权码并将其转换为访问令牌和刷新令牌时,我们调用 AuthorizationMgr
类上的 SetCachedRefreshToken
方法。
/// <summary>
/// Saves a refresh token to the specified storage name,
/// and encrypts it using the specified key.
/// </summary>
public static void SetCachedRefreshToken(string storageName,
string key,
IAuthorizationState state)
{
// Create the file content.
string scopes = state.Scope.Aggregate("", (left, append) => left + " " + append);
string content = scopes + "\r\n" + state.RefreshToken;
// Encrypt it.
byte[] salt = Encoding.Unicode.GetBytes(Assembly.GetEntryAssembly().FullName + key);
byte[] encrypted = ProtectedData.Protect(
Encoding.Unicode.GetBytes(content), salt, DataProtectionScope.CurrentUser);
// Save the data to the auth file.
string file = storageName + ".auth";
AppData.WriteFile(file, encrypted);
}
此方法将接收一个存储名称和一个加密密钥以及我们收到的凭据,并将它们以加密文件的形式存储在应用程序数据文件夹中。在我的示例代码中,文件名和密钥嵌入在源代码中……我们都知道这不安全。您应该想出更好的方法来处理这个问题……至少密钥不应该在源代码中。
这些授权信息是敏感的,因为它包含刷新令牌。这是我们无需用户明确授权即可获取当前访问令牌的原因。当初始访问被批准时,会生成刷新令牌,它代表了该访问请求和明确的授权。它将来可以作为用户授权的代理。AuthorizationMgr
类上还有一个名为 GetCachedRefreshToken
的方法,它将从加密文件中检索该令牌。当程序尝试获取授权以对 Google Drive 执行操作时,代码会尝试使用此方法加载缓存的刷新令牌。如果成功,NativeApplicationClient
类有一个名为 RefreshToken
的方法,该方法接收缓存的授权信息并从 Google 请求一个新的访问令牌。如果成功,程序将获得静默授权,API 访问将被授予。
使用代码
困难的部分已经结束。获得应用程序授权和有效的访问令牌是最困难的部分。包含的 WPF 项目还展示了如何获取用户驱动器中的所有文件,以检查文件是否存在。上传或更新文件的 API 调用也包含在项目中。
开始使用 Google API 访问
访问任何 Google API 的第一件事是在 API 控制台中设置一个项目并生成 OAuth 凭据。如果您有 Google 帐户,可以在Google API Console 登录。登录后,您必须创建一个新项目。创建项目后,您将看到您的项目已分配了 API 密钥。这是您项目的母密钥。您的项目最多可以授权七个应用程序访问。(这些应用程序可以是不同的类型;已安装、Web、移动等)
在您的应用程序能够访问 Google Drive 或大多数其他感兴趣的服务之前,您必须创建一个 OAuth2.0 客户端 ID。可以在左侧菜单的“API 访问”下找到。如果不明显,请点击 API 控制台中的蓝色大按钮。这将启动生成 OAuth2 客户端凭据的过程。对于不同的应用程序类型,过程会有所不同。如果您不是在开发像本例这样的已安装应用程序,情况可能会有所不同。
该过程以提供应用程序名称开始。
产品名称将在授权请求显示给用户时在授权页面上显示。您可以选择性地指定产品徽标所在的 URL,该 URL 也将显示在该页面上。在本文章前面的图片中,您可以看到我为我的示例应用程序生成的页面,以了解最终外观。
当您单击“下一步”时,您需要向 Google 服务器提供有关您应用程序的信息。在我们的例子中,我们将指定它是一个已安装的应用程序,并且我们将在“其他”上运行它。
创建客户端 ID 后,我们将返回到主控制台,应该会看到我们的新 OAuth 凭据。
在屏幕截图中,您可以看到客户端 ID 和客户端密钥的位置。这些值将放入示例代码的 ClientCredentials
类中。这两个值将您的应用程序标识为您刚刚在 API 控制台中设置的应用程序。您应该尽最大努力保守这些值的秘密,并知道将它们嵌入代码中并不是理想的做法。
现在,您的应用程序已在 Google 中注册,并且您已将客户端 ID 和客户端密钥放入代码中,您必须告诉 Google 您的应用程序将要访问哪些服务。如果您不执行此步骤,在尝试访问特定 API 时将收到 401 未授权错误。授予访问权限非常简单,在控制台左侧的“服务”选项下完成。您在此处启用和禁用的服务访问是项目范围的(非应用程序特定),并且决定了请求的权限列表的外观。要访问 Google Drive,我已经启用了 Drive Service 和 Drive SDK。
API 访问有一些限制和其他条款。您应该参考 Google 的文档,以确保您了解这些限制。
完成这些步骤后,您的应用程序应该能够访问 Google Drive,因此让我们转向代码。
使用示例代码
尽管这是一个 WPF 应用程序,但代码对于 WinForms 应用程序或类似应用程序也是相同的。示例项目只是创建一个带有“GO”按钮的窗口。主页面的代码隐藏中的事件处理程序仅调用静态 MainClass
中的 AuthorizeAndUpload
方法。
/// <summary>
/// This is the worker method that executes when the user clicks the GO button.
/// It illustrates the workflow that would need to take place in an actual application.
/// </summary>
public static void AuthorizeAndUpload()
{
// First, create a reference to the service you wish to use.
// For this app, it will be the Drive service. But it could be Tasks, Calendar, etc.
// The CreateAuthenticator method is passed to the service which will use that when it is time to authenticate
// the calls going to the service.
_service = new DriveService(CreateAuthenticator());
// Open a dialog box for the user to pick a file.
OpenFileDialog dialog = new OpenFileDialog();
dialog.AddExtension = true;
dialog.DefaultExt = ".txt";
dialog.Filter = "Text files (*.txt)|*.txt|All files (*.*)|*.*";
dialog.Multiselect = false;
dialog.ShowDialog();
File body = new File();
body.Title = System.IO.Path.GetFileName(dialog.FileName);
body.Description = "A test document";
body.MimeType = "text/plain";
System.IO.Stream fileStream = dialog.OpenFile();
byte[] byteArray = new byte[fileStream.Length];
fileStream.Read(byteArray, 0, (int)fileStream.Length);
System.IO.MemoryStream stream = new System.IO.MemoryStream(byteArray);
// Get a listing of the existing files...
List<File> fileList = Utilities.RetrieveAllFiles(_service);
foreach (File item in fileList)
{
if (item.Title == body.Title)
{
// File exists in the drive already!
MessageBoxResult result = System.Windows.MessageBox.Show("The file you picked already exists " +
"in your Google Drive. Do you wish to overwrite it?",
"Confirmation", MessageBoxButton.YesNoCancel);
if (result == MessageBoxResult.Yes)
{
// Yes... overwrite the file
Utilities.UpdateFile(_service, item.Id, item.Title,
item.Description, item.MimeType, dialog.FileName, true);
}
else if (result == MessageBoxResult.No)
{
// No code here
Utilities.InsertFile(_service, System.IO.Path.GetFileName(dialog.FileName),
"An uploaded document", "", "text/plain", dialog.FileName);
}
else
{
// Cancel code here
return;
}
break;
}
}
System.Windows.MessageBox.Show("Upload Complete");
}
我们首先创建一个新的 DriveService
对象。这是 Google 库提供的一个类,用于封装 Drive API。如果您正在处理不同的服务,例如 Tasks,那么它将是一个不同的对象,例如 TaskService
。类构造函数的参数是实现 IAuthenticator
接口的对象。在这种情况下,我们正在使用 CreateAuthenticator
函数的结果来传递 Authenticator 对象。CreateAuthenticator
的外观如下:
private static IAuthenticator CreateAuthenticator()
{
var provider = new NativeApplicationClient(GoogleAuthenticationServer.Description);
provider.ClientIdentifier = ClientCredentials.CLIENT_ID;
provider.ClientSecret = ClientCredentials.CLIENT_SECRET;
return new OAuth2Authenticator<NativeApplicationClient>(provider, GetAuthorization);
}
您可以看到 CreateAuthenticator
首先创建一个 NativeApplicationClient
。如前所述,它为我们封装了许多身份验证机制。您可以看到接下来的两行从我们的静态 ClientCredentials
类设置了 ClientID
和 Secret。我们做的最后一件事是使用 NativeApplicationClient
创建一个类型化的 OAuth2Authenticator
(它实现了 IAuthenticator
接口),然后返回。
您可能最初会忽略 OAuth2Authenticator
构造函数中对 GetAuthorization
的引用。该参数需要一个实现 IAuthorizationState
接口的对象。该接口包含当前的授权信息,NativeApplicationClient
使用它在后续 API 调用中提供访问令牌。
/// <summary>
/// Method to get the authorization from the user to access their Google Drive from the application
/// </summary>
/// <param name="client"></param>
/// <returns></returns>
private static IAuthorizationState GetAuthorization(NativeApplicationClient client)
{
// You should use a more secure way of storing the key here as
// .NET applications can be disassembled using a reflection tool.
const string STORAGE = "gdrive_uploader";
const string KEY = "z},drdzf11x9;87";
string scope = DriveService.Scopes.Drive.GetStringValue();
// Check if there is a cached refresh token available.
IAuthorizationState state = AuthorizationMgr.GetCachedRefreshToken(STORAGE, KEY);
if (state != null)
{
try
{
client.RefreshToken(state);
return state; // Yes - we are done.
}
catch (DotNetOpenAuth.Messaging.ProtocolException ex)
{
Debug.WriteLine("Using existing refresh token failed: " + ex.Message);
}
}
// If we get here, there is no stored token. Retrieve the authorization from the user.
state = AuthorizationMgr.RequestNativeAuthorization(client, scope);
AuthorizationMgr.SetCachedRefreshToken(STORAGE, KEY, state);
return state;
}
GetAuthorization
将首先尝试通过使用 AuthorizationMgr
辅助类来检索缓存的凭据。如果没有缓存的凭据,该方法将使用 AuthorizationMgr.RequestNativeAuthorization
函数获取用户授权码,然后将其交换为有效的访问令牌和刷新令牌,这些令牌将通过 AuthorizationMgr
辅助类上的 SetCachedRefreshToken
方法保存。这是在我们没有缓存凭据时会打开 Web 浏览器并请求授权的过程的一部分。
虽然授权机制在开始时已设置和定义,但实际上直到稍后的第一次 API 访问才执行。回到 AuthorizeAndUpload
方法,我们已经创建了 Drive Service,并将所有这些对象和方法传递给处理身份验证。接下来,我们提示用户选择要上传的文件。这是通过标准的打开文件对话框完成的。获得文件后,我们必须将其转换为 Google 文件对象。接下来的几行设置该文件并通过内存流复制内容。
File body = new File();
body.Title = System.IO.Path.GetFileName(dialog.FileName);
body.Description = "A test document";
body.MimeType = "text/plain";
System.IO.Stream fileStream = dialog.OpenFile();
byte[] byteArray = new byte[fileStream.Length];
fileStream.Read(byteArray, 0, (int)fileStream.Length);
System.IO.MemoryStream stream = new System.IO.MemoryStream(byteArray);
下一段代码调用 API 来检索用户 Google Drive 中的所有文件。在我的示例项目中,我调用的是该 API 的同步版本,并在 UI 线程上执行……这两种都不是好的编程实践,但这只是一个示例……不是生产环境。显然,您应该以异步方式调用此方法,并将结果正确地传送到 UI。
// Get a listing of the existing files...
List<File> fileList = Utilities.RetrieveAllFiles(_service);
此调用将返回一个 Google 文件对象列表,它将是用户驱动器中的*所有文件*。包括子文件夹中的文件。每个文件都有一个父 ID 属性,可用于重新创建驱动器的层次结构。我的示例只是查看要上传的文件是否已存在于驱动器中。实际上,此逻辑存在一个缺陷,因为示例只上传到驱动器的根目录。如果目标文件存在于子文件夹中,它将被报告给用户为已存在,并要求用户覆盖,但实际上,由于它们位于不同的级别,因此不会被覆盖。生产应用程序需要考虑这一点。
foreach (File item in fileList)
{
if (item.Title == body.Title)
{
// File exists in the drive already!
MessageBoxResult result = System.Windows.MessageBox.Show("The file you picked already exists " +
"in your Google Drive. Do you wish to overwrite it?",
"Confirmation", MessageBoxButton.YesNoCancel);
if (result == MessageBoxResult.Yes)
{
// Yes... overwrite the file
Utilities.UpdateFile(_service, item.Id, item.Title,
item.Description, item.MimeType, dialog.FileName, true);
}
else if (result == MessageBoxResult.No)
{
// No code here
Utilities.InsertFile(_service, System.IO.Path.GetFileName(dialog.FileName),
"An uploaded document", "", "text/plain", dialog.FileName);
}
else
{
// Cancel code here
return;
}
break;
}
}
简单的 foreach
循环遍历列表,查看文件名是否匹配。如果匹配,我们需要知道,因为 Drive API 在这里有点不同。如果文件不存在,您应该将其*插入*到 Drive 中。您可以在上面看到调用该函数的代码。如果文件存在,您需要决定是更新当前文件还是上传一个并排的版本。如果您要用新数据覆盖现有文件,则必须调用 UpdateFile
方法并传入新信息。有趣的是,您可以仅用新元数据“更新”文件而无需更改实际内容。如果您要重命名文件或更改说明,这就是您要做的。当然,如果您只是更新内容,您将使用所有现有元数据。
或者,如果文件存在,您仍然可以使用*插入*方法再次上传文件。区别在于 Google 将创建文件的另一个版本。因此,如果 foo.txt 存在,Drive 服务将上传 foo.txt 并将其保存为驱动器中的 foo(1).txt。后续上传将简单地增加附加到文件名上的数字。对于生产应用程序,了解此行为的后果很重要。您可能不想创建用户文件的 99 个副本!
在示例代码中,用户对消息框决定的结果决定了我们将如何上传他们选择的文件。上传或插入文件的方法会处理将文件传输到 Drive 的过程。
/// <summary>
/// Inserts a new file into the Google Drive.
/// </summary>
/// <param name="service">Drive API service instance.</param>
/// <param name="title">Title (name) of the file to insert, including the extension.</param>
/// <param name="description">Description of the file to insert.</param>
/// <param name="parentId">Parent folder's ID. (Empty string to put the file in the drive root)</param>
/// <param name="mimeType">MIME type of the file to insert.</param>
/// <param name="filename">Filename (including path) of the file to upload relative to the local machine.</param>
/// <returns>Inserted file metadata, null is returned if an API error occurred.</returns>
/// <remarks>The filename passed to this function should be the entire path and filename (with extension) for the source file on the
/// local machine. The API will put the file in the Google Drive using the "Title" property.</remarks>
public static Google.Apis.Drive.v2.Data.File InsertFile(DriveService service,
String title, String description, String parentId, String mimeType, String filename)
{
// File's metadata.
Google.Apis.Drive.v2.Data.File body = new Google.Apis.Drive.v2.Data.File();
body.Title = title;
body.Description = description;
body.MimeType = mimeType;
// Set the parent folder.
if (!String.IsNullOrEmpty(parentId))
{
body.Parents = new List<ParentReference>() { new ParentReference() { Id = parentId } };
}
// Load the File's content and put it into a memory stream
byte[] byteArray = System.IO.File.ReadAllBytes(filename);
MemoryStream stream = new MemoryStream(byteArray);
try
{
// When we add a file, we create an Insert request then call the Upload method on the request.
// (If we were updating an existing file, we would use the Update function)
FilesResource.InsertMediaUpload request = service.Files.Insert(body, stream, mimeType);
request.Upload();
// Set the file object to the response of the upload
Google.Apis.Drive.v2.Data.File file = request.ResponseBody;
// Uncomment the following line to print the File ID.
// Console.WriteLine("File ID: " + file.Id);
// return the file object so the caller has a reference to it.
return file;
}
catch (Exception e)
{
// May want to log this or do something with it other than just dumping to the console.
Console.WriteLine("An error occurred: " + e.Message);
return null;
}
}
此函数中最有趣的是 FileResource.InsertMediaUpload
请求对象。此对象将插入新文件到 Drive 所需的信息打包在一起。类似地,UpdateFile
方法使用 UpdateMediaUpload
对象来帮助它。两者都通过 DriveService.Files
对象中的具体对象进行实例化。同样,如果您正在处理像 Tasks 这样的其他 API,那么这些将是 InsertTaskUpload
方法和 TaskService.Tasks
对象。Upload 方法执行实际的上传。因为请求来自 DriveService
,并且因为 Drive Service 是使用身份验证方法设置的,所以在 Upload 方法的后台有很多事情正在发生。这里只需要知道一切都在 Google 的库中,我们可以相信它都会正常工作。当我们在第一次执行上传(或文件列表)命令时,我们 MainClass
中的 CreateAuthenticator
和 GetAuthorization
方法可能会被调用。
成功后,该方法将返回一个 Google 文件对象,它是远程对象的本地代理。该对象包含关键信息,例如文件的 ID 号及其父 ID。这些信息可以在后续操作中使用,以在不通过文件列表方法查找的情况下操作文件。
尽管您需要检查文件是否存在并处理文件不再存在的情况(如同您保存信息时一样),但您可以将文件信息本地持久化,以便下次运行时可以快速访问。除非用户删除并重新创建文件,否则文件 ID 不会改变。
上传方法完成后,就完成了。唯一剩下的是告诉用户文件已上传。很明显,作为如何执行此操作的示例,为了清晰起见,许多重要的事情都被省略了。生产应用程序应该将其中许多内容异步处理,并在进行过程中通知用户进度。还应该添加错误处理,因为像用户未授予访问权限这样简单的事情可能会给整个事情带来很大的麻烦。应用程序应检测到这种情况并优雅地失败。最后,可能应该使用比标记为“GO”的大按钮更好的东西……
结论
一旦我掌握了授权步骤并找到了存储和重用缓存凭据的方法,Drive API 就变得非常容易使用。事实上,在 .NET 中使用几乎任何 Google API 都相当容易,前提是您能正确处理授权部分。已安装的应用程序会带来挑战,我希望本文和示例项目能为您在应用程序中添加 Google API 调用节省大量时间和精力。祝您好运!
历史
- 2012 年 11 月 2 日 - 初始修订。
- 2012 年 12 月 10 日 - 修复了代码中文件不存在时未上传的错误。清理了 Google API 项目文件夹中未使用的 Google DLL。