Android: 下载 Google Drive 文件到本地 | 教程
2025-02-03 22:29:22
Android程序化下载和保存 Google Drive 中选定文件到本地目录
当需要在 Android 应用程序中集成 Google Drive API,并允许用户选择和下载文件到本地存储时,开发者经常会遇到一些挑战。 这篇文章讨论了如何通过编程方式从 Google Drive 下载所选文件并将其保存到本地目录。 提供的示例代码将有助于理解实现过程,并避开一些常见的坑。
问题分析
从 Google Drive 下载文件,主要遇到的问题在于,虽然可以通过 DriveId
和 DriveFile
访问到文件,但实际的文件内容读取和保存却需要额外的步骤。 直接使用这些对象并不能直接获取文件的数据流,需要进一步利用 Google Drive API 提供的功能。
解决方案
提供两种方法,分别使用 Google Drive API v3 和 Google Play Services 的 Drive API 实现文件的下载,适用于不同场景,各有利弊,可以选择最适合自身项目的方案。
方案一:使用 Google Drive API v3
此方案绕过 Google Play Services Drive API,直接使用 Google Drive API v3。它需要用户授权并通过网络请求下载文件,更为通用,也更灵活。
操作步骤:
-
添加依赖 : 在
build.gradle
文件中添加 Google API Client 依赖。dependencies { implementation 'com.google.api-client:google-api-client-android:1.32.1' implementation 'com.google.apis:google-api-services-drive:v3-rev20210811-1.32.1' implementation 'com.google.oauth-client:google-oauth-client-java6:1.32.1' implementation 'com.google.auth:google-auth-library-oauth2-http:0.27.0' }
-
权限申请 : 确保应用程序拥有网络访问和存储权限。在
AndroidManifest.xml
文件中声明:<uses-permission android:name="android.permission.INTERNET"/> <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>
-
文件下载函数 : 创建一个函数来下载文件。它需要
DriveId
、本地存储路径以及访问令牌。import com.google.api.client.http.GenericUrl; import com.google.api.client.http.HttpResponse; import com.google.api.client.googleapis.auth.oauth2.GoogleCredential; import com.google.api.client.http.javanet.NetHttpTransport; import com.google.api.client.json.jackson2.JacksonFactory; import java.io.File; import java.io.FileOutputStream; import java.io.IOException; import java.io.InputStream; public class DriveDownloadHelper { public static boolean downloadFile(String fileId, File localFile, String accessToken) { try { NetHttpTransport transport = new NetHttpTransport(); JacksonFactory jsonFactory = JacksonFactory.getDefaultInstance(); GoogleCredential credential = new GoogleCredential().setAccessToken(accessToken); String downloadUrl = "https://www.googleapis.com/drive/v3/files/" + fileId + "?alt=media"; GenericUrl url = new GenericUrl(downloadUrl); HttpResponse response = transport.createRequestFactory(credential).buildGetRequest(url).execute(); InputStream inputStream = response.getContent(); FileOutputStream outputStream = new FileOutputStream(localFile); byte[] buffer = new byte[4096]; int bytesRead; while ((bytesRead = inputStream.read(buffer)) != -1) { outputStream.write(buffer, 0, bytesRead); } outputStream.close(); inputStream.close(); return true; } catch (IOException e) { e.printStackTrace(); return false; } } }
这个函数使用 Google API Client 库构建一个带有授权头的 HTTP 请求,然后下载文件。 它从Google Drive获取InputStream并将其写入提供的 File 对象。
-
获取 Access Token : 你需要一个有效的 Access Token 才能授权 API 请求。可以参考 Google OAuth 2.0 的文档来获取。
// 示例,不建议硬编码 String accessToken = "YOUR_ACCESS_TOKEN";
应该通过 OAuth 2.0 流程从 Google 获取有效的 Access Token,而不要硬编码。
-
调用下载 : 在
onActivityResult
中调用下载函数:@Override protected void onActivityResult(int requestCode, int resultCode, Intent data) { super.onActivityResult(requestCode, resultCode, data); if (resultCode == RESULT_OK) { if (requestCode == REQUEST_CODE_OPENER) { DriveId driveId = data.getParcelableExtra(OpenFileActivityBuilder.EXTRA_RESPONSE_DRIVE_ID); String fileId = driveId.getResourceId(); File localFile = new File(getExternalFilesDir(null), "downloaded_file.pdf"); // 或者 .docx 等 //在后台线程下载 new Thread(() -> { boolean success = DriveDownloadHelper.downloadFile(fileId, localFile, accessToken); runOnUiThread(() -> { if (success) { // 下载成功处理 } else { // 下载失败处理 } }); }).start(); } } }
务必在后台线程执行下载操作,防止阻塞UI线程。下载完成后,在主线程更新 UI。
方案二:使用 Google Play Services Drive API
如果应用程序已经集成了 Google Play Services,并且使用了其提供的 Drive API,可以通过该 API 获取文件内容。这种方法更直接,也更容易集成,但依赖于 Google Play Services。
操作步骤:
-
修改 onActivityResult : 获取
DriveFile
后,创建一个DownloadAsyncTask
下载文件。@Override protected void onActivityResult(int requestCode, int resultCode, Intent data) { super.onActivityResult(requestCode, resultCode, data); if (resultCode == RESULT_OK) { switch (requestCode) { case REQUEST_CODE_OPENER: DriveId driveId = data.getParcelableExtra( OpenFileActivityBuilder.EXTRA_RESPONSE_DRIVE_ID); DriveFile file = driveId.asDriveFile(); File localFile = new File(getExternalFilesDir(null), "downloaded_file.pdf"); new DownloadAsyncTask(mGoogleApiClient, file, localFile).execute(); break; case REQUEST_CODE_RESOLUTION: mGoogleApiClient.connect(); break; } } }
-
创建 AsyncTask : 创建一个继承自
AsyncTask
的类来处理文件下载:import android.os.AsyncTask; import android.util.Log; import com.google.android.gms.common.api.GoogleApiClient; import com.google.android.gms.common.api.ResultCallback; import com.google.android.gms.drive.DriveFile; import com.google.android.gms.drive.DriveApi; import com.google.android.gms.drive.MetadataBuffer; import com.google.android.gms.drive.MetadataChangeSet; import java.io.File; import java.io.FileOutputStream; import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; class DownloadAsyncTask extends AsyncTask<Void, Void, Boolean> { private GoogleApiClient mGoogleApiClient; private DriveFile mFile; private File mLocalFile; public DownloadAsyncTask(GoogleApiClient googleApiClient, DriveFile file, File localFile) { this.mGoogleApiClient = googleApiClient; this.mFile = file; this.mLocalFile = localFile; } @Override protected Boolean doInBackground(Void... params) { try { DriveApi.DriveContentsResult driveContentsResult = mFile.open(mGoogleApiClient, DriveFile.MODE_READ_ONLY, null).await(); if (!driveContentsResult.getStatus().isSuccess()) { return false; } InputStream inputStream = driveContentsResult.getDriveContents().getInputStream(); OutputStream outputStream = new FileOutputStream(mLocalFile); byte[] buffer = new byte[4096]; int bytesRead; while ((bytesRead = inputStream.read(buffer)) != -1) { outputStream.write(buffer, 0, bytesRead); } inputStream.close(); outputStream.close(); driveContentsResult.getDriveContents().discard(mGoogleApiClient); // 记得关闭并释放资源 return true; } catch (IOException e) { Log.e("DownloadAsyncTask", "Error downloading file", e); return false; } } @Override protected void onPostExecute(Boolean result) { if (result) { // 下载成功 Log.i("DownloadAsyncTask", "File downloaded to: " + mLocalFile.getAbsolutePath()); } else { // 下载失败 Log.e("DownloadAsyncTask", "File download failed"); } } }
这个
AsyncTask
在后台线程打开DriveFile
,获取其InputStream
,然后将其写入本地文件。请务必关闭流并discard
DriveContents
以释放资源。
安全建议
- 权限控制 : 仔细评估应用需要的 Google Drive 权限。 只请求所需的最小权限。
- 数据加密 : 如果下载的文件包含敏感信息,考虑在存储到本地之前对其进行加密。
- 异常处理 : 完善错误处理机制,优雅地处理网络连接问题、权限问题等。
总结
这篇文章提供了两种从 Google Drive 下载文件并在 Android 应用中保存的方案,各具优势。在方案选择上,需要考虑现有项目的依赖以及希望拥有的灵活性,保证文件可以安全地保存到本地。