Kotlin服务端应用使用Google Drive服务账号认证上传文件时遭遇401 Unauthorized错误及相关优化需求
嗨,我来帮你一步步解决这个问题,同时满足你对API升级和序列化方式的需求:
一、先搞定401 Unauthorized的核心问题
你遇到的401错误主要有两个关键原因,咱们逐一排查解决:
1. 服务账号缺少写入权限的Scope
你的旧代码能读取目录是因为用了drive.readonly的Scope,但上传文件需要写入权限。你第一份代码里的GoogleCredentials没有指定Scope,默认权限不足,所以要明确添加写入相关的Scope:
val credentials = GoogleCredentials.fromStream(FileInputStream("gdrive-admin.json")) .createScoped(listOf("https://www.googleapis.com/auth/drive")) // 全权限,也可以用drive.file更细粒度
如果你不想给全权限,可以用
https://www.googleapis.com/auth/drive.file,但需要确保上传目标文件夹已经共享给服务账号邮箱。
2. 服务账号的文件存储位置问题
服务账号有自己独立的云端硬盘空间,你共享给它的文件夹需要明确指定为上传文件的父目录,否则文件会上传到服务账号自己的盘里(你看不到,但其实可能上传成功了)。修改文件元数据,添加父文件夹ID:
val fileMetadata = File() fileMetadata.setName("img3.jpg") fileMetadata.parents = listOf("你的共享文件夹ID") // 替换成你实际共享的文件夹ID
二、替换已废弃的GoogleCredential
你提到GoogleCredential已经被废弃,现在官方推荐使用GoogleCredentials(就是你第一份代码里的类),结合上面的Scope配置,就能替代旧代码的功能,同时避免废弃API的警告。
三、切换到Kotlin Serialization替代Gson
要替换GsonFactory为Kotlin Serialization,需要做以下几步:
1. 添加依赖
在你的build.gradle.kts(或build.gradle)里添加Google API Client对Kotlin Serialization的支持:
// Gradle Kotlin DSL示例 implementation("com.google.api-client:google-api-client-kotlin-serialization:1.44.1") implementation("org.jetbrains.kotlinx:kotlinx-serialization-json:1.6.0") // 确保序列化核心库版本匹配
同时要确保已经启用Kotlin Serialization插件:
plugins { kotlin("jvm") version "1.9.0" kotlin("plugin.serialization") version "1.9.0" }
2. 修改代码中的JsonFactory
把GsonFactory.getDefaultInstance()替换为KotlinSerializationJsonFactory:
import com.google.api.client.json.kotlin.KotlinSerializationJsonFactory // ... val jsonFactory = KotlinSerializationJsonFactory() val service = Drive.Builder(NetHttpTransport(), jsonFactory, requestInitializer) .setApplicationName("Drive samples").build()
整合后的完整可运行代码
把以上修改整合到一起,最终的上传代码应该是这样的:
import com.google.api.client.googleapis.auth.oauth2.GoogleCredentials import com.google.api.client.http.HttpCredentialsAdapter import com.google.api.client.http.javanet.NetHttpTransport import com.google.api.client.json.kotlin.KotlinSerializationJsonFactory import com.google.api.services.drive.Drive import com.google.api.services.drive.model.File import com.google.api.services.drive.model.FileContent import java.io.FileInputStream fun uploadBasic(): String? { // 加载服务账号凭证并添加写入权限Scope val credentials = GoogleCredentials.fromStream(FileInputStream("gdrive-admin.json")) .createScoped(listOf("https://www.googleapis.com/auth/drive")) val requestInitializer = HttpCredentialsAdapter(credentials) // 使用Kotlin Serialization构建Drive服务 val service = Drive.Builder(NetHttpTransport(), KotlinSerializationJsonFactory(), requestInitializer) .setApplicationName("Drive samples").build() // 配置文件元数据,指定上传到共享文件夹 val fileMetadata = File() fileMetadata.setName("img3.jpg") fileMetadata.parents = listOf("替换为你的共享文件夹ID") // 这里要填实际的文件夹ID // 指定文件类型和路径 val mediaContent = FileContent("image/jpeg", java.io.File("images/img3.jpg")) return try { val file = service.files().create(fileMetadata, mediaContent).setFields("id").execute() println("File ID: " + file.id) file.id } catch (e: com.google.api.client.googleapis.json.GoogleJsonResponseException) { System.err.println("Unable to upload file: " + e.details) throw e } }
额外注意点
- 共享文件夹给服务账号时,要确保服务账号的邮箱(在
gdrive-admin.json里的client_email字段)被添加为文件夹的编辑者或所有者。 - 如果用
drive.file的Scope,上传前需要确保服务账号已经有该文件夹的访问权限(通过共享操作),否则还是会报错。
备注:内容来源于stack exchange,提问作者Psijic

