Android应用上传视频至Google Drive报无法创建/tokens目录如何解决?
问题解答
关于目录创建的疑问确认
- 你的理解正确:程序确实是在本地设备存储中创建tokens目录,和Google Drive远程服务器无关。
- 目录作用:该目录用来持久化存储用户授权后得到的OAuth2凭证(包括access_token、refresh_token),避免每次使用Drive功能都需要用户重新登录授权。
报错原因
你直接照搬了面向桌面端Java环境的Drive官方示例,和Android系统的规则不兼容:
- Android应用没有权限在系统根目录直接创建
/tokens目录,你写的固定路径"tokens"默认会指向根目录,触发权限不足报错。 - 你用的
AuthorizationCodeInstalledApp、LocalServerReceiver类都是桌面端用来处理授权跳转的组件,Android系统不支持这种本地端口监听的授权逻辑,就算解决了目录问题后续也会报错。
修复方案
1. 修复tokens目录路径
将固定路径替换为Android应用专属内部存储路径,应用对该目录有完全读写权限,无需额外申请存储权限:
// 替换getCredentials方法中创建FileDataStoreFactory的逻辑 File tokenDir = new File(context.getFilesDir(), "tokens"); if (!tokenDir.exists()) { tokenDir.mkdirs(); // 手动创建目录,避免创建失败 } GoogleAuthorizationCodeFlow flow = new GoogleAuthorizationCodeFlow.Builder( HTTP_TRANSPORT, JSON_FACTORY, clientSecrets, SCOPES) .setDataStoreFactory(new FileDataStoreFactory(tokenDir)) .setAccessType("offline") .build();
2. 替换授权方式(Android环境推荐方案)
废弃桌面端授权逻辑,改用Android专属的Google Sign-In SDK完成Drive授权:
- 先在Google Cloud控制台给你的Android应用配置包名、签名SHA1值,生成Android专属的OAuth凭证,不要用桌面端的credentials.json。
- 集成Google Sign-In依赖,构建授权请求时申请Drive写入权限:
你当前的权限DriveScopes.DRIVE_METADATA_READONLY是只读权限,无法完成上传,需要替换为DriveScopes.DRIVE_FILE(仅访问应用自己创建的文件)或DriveScopes.DRIVE(访问用户所有Drive文件)。
3. 修复文件大小校验逻辑
你当前的getFileSizeInMB方法直接用Uri的path创建File是错误的,Content Uri的路径不是本地真实文件路径,需要通过ContentResolver查询:
private long getFileSizeFromUri(Uri uri) { Cursor cursor = getContext().getContentResolver().query(uri, new String[]{OpenableColumns.SIZE}, null, null, null); if (cursor != null && cursor.moveToFirst()) { long size = cursor.getLong(0); cursor.close(); return size / (1024 * 1024); // 转成MB } return 0; }
4. 视频上传代码示例
拿到用户选择的视频Uri后,通过Drive服务上传:
private void uploadVideo(Uri videoUri) throws IOException { ContentResolver resolver = getContext().getContentResolver(); String mimeType = resolver.getType(videoUri); // 获取文件名 String fileName = null; Cursor cursor = resolver.query(videoUri, new String[]{OpenableColumns.DISPLAY_NAME}, null, null, null); if (cursor != null && cursor.moveToFirst()) { fileName = cursor.getString(0); cursor.close(); } // 构建Drive文件元数据 com.google.api.services.drive.model.File fileMeta = new com.google.api.services.drive.model.File(); fileMeta.setName(fileName); // 读取文件流上传 try (InputStream inputStream = resolver.openInputStream(videoUri)) { InputStreamContent content = new InputStreamContent(mimeType, inputStream); driveService.files().create(fileMeta, content) .setFields("id,name") .execute(); Toast.makeText(getContext(), "视频上传成功", Toast.LENGTH_SHORT).show(); } }
注意:Drive服务初始化、上传操作都需要放在子线程执行,不要直接在主线程运行,否则会触发ANR报错。
内容的提问来源于stack exchange,提问作者vesii
相关产品推荐
相关产品推荐

