☰
Android 获取手机本地所有 PDF 文件:TaoToken 统一 Key 接入与配置骨架
2026/9/27 15:10:42 网站建设 项目流程

1. Android 本地 PDF 扫描到底难在哪

Android 获取手机本地所有 PDF 文件,本质上是两件事:一是用 MediaStore 把散落在各目录的 PDF 索引出来,二是把这份文件清单交给 AI 工具做后续处理(摘要、分类、问答)。第一件事网上教程很多,第二件事才是真正卡人的地方——你本地跑通了扫描,结果要接大模型时发现每个工具都要单独填 Key、单独配 Base URL,Cline 一套、Claude Code 一套、脚本里又一套,改一次配置要翻五个文件。

这篇就按「扫描 PDF → 拿到路径列表 → 用 TaoToken 统一 Key 接入 AI 工具」这条链路走一遍。适合两类人:正在做 Android 文件管理类 App、需要把本地文档喂给模型的开发者;以及想用 Cline、Claude Code 这类编码工具批量处理本地 PDF 资料的人。核心检索词就三个:Android 本地 PDF 扫描、MediaStore 查询、TaoToken 统一 Key 接入。

先说结论:扫描部分用MediaStore.Files配合 MIME 过滤是最稳的路径,Android 10 之后分区存储(Scoped Storage)让直接读绝对路径变得不可靠,所以我会同时给「拿 Uri」和「拿可读路径」两种写法。AI 接入部分,TaoToken 提供的是 OpenAI 兼容的统一通道,一个 Key 可以给多个工具复用,省掉到处填配置的麻烦。下面从权限开始,一步步给可复制的代码和配置。

2. TaoToken 前置:统一 Key 与通道准备

在写扫描代码之前,先把 AI 侧的通道准备好,这样扫描出结果后能立刻验证「文件清单 → 模型调用」这条链路是通的。TaoToken 的定位是一个统一 Key/API 通道,官网在 https://taotoken.net/?utm_source=taotoken_aicg_blog_end&utm_medium=csdn&utm_campaign=rewrite&utm_content= ,API 入口是 https://taotoken.net/api (这个地址不加 UTM 参数,配置里直接填它)。

你需要做的准备动作只有三步。第一步,进控制台创建 API Key,地址是 https://taotoken.net/console?utm_source=taotoken_aicg_blog_end&utm_content=console&utm_campaign=rewrite ,创建后在密钥管理页复制出来,形如sk-开头的一串。第二步,确认你要用的模型名,模型对话页 https://taotoken.net/models?utm_source=taotoken_aicg_blog_end&utm_content=models&utm_campaign=rewrite 里能看到当前可用的模型列表,记下你打算调用的那个。第三步,如果你打算长期用编码类工具(Cline、Claude Code 等)批量处理 PDF,建议直接看 Coding Plan,地址 https://taotoken.net/coding-plan?utm_source=taotoken_aicg_blog_end&utm_content=coding-plan&utm_campaign=rewrite ,它比按量计费更适合高频调用场景。

注意:API Key 只存在本地配置文件或环境变量里,不要硬编码进 Android 工程提交到 Git。下面所有配置示例里的sk-xxxx都请替换成你自己的 Key。

这里有个容易混淆的点:TaoToken 的 API 地址是https://taotoken.net/api,在 OpenAI 兼容的客户端里,Base URL 通常填这个,然后客户端会自动拼/v1/chat/completions。如果你用的工具要求填完整 endpoint,那就填https://taotoken.net/api/v1/chat/completions。两种写法取决于工具,下面配置骨架里我会标注清楚。

3. 可复制配置:扫描代码 + 工具接入骨架

3.1 Android 侧:MediaStore 扫描 PDF 的完整实现

先在AndroidManifest.xml里声明权限。Android 13(API 33)之后读媒体文件要用细分权限,所以新旧版本都要覆盖:

<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" android:maxSdkVersion="32" /> <uses-permission android:name="android.permission.READ_MEDIA_IMAGES" /> <uses-permission android:name="android.permission.READ_MEDIA_VIDEO" /> <uses-permission android:name="android.permission.READ_MEDIA_AUDIO" />

PDF 不属于图片/视频/音频这三类细分媒体,在 Android 13+ 上通过MediaStore.Files查询application/pdf仍然可行,但部分机型需要走MANAGE_EXTERNAL_STORAGE或 SAF(存储访问框架)才能拿到全部文件。实测下来,用MediaStore.Files查 MIME 是最省事的路径,先跑通它,遇到权限不足再降级到 SAF。

下面是 Kotlin 扫描函数,返回文件名和内容 Uri 的配对列表:

data class PdfItem(val name: String, val uri: Uri, val size: Long) fun scanLocalPdfs(context: Context): List<PdfItem> { val result = mutableListOf<PdfItem>() val collection = MediaStore.Files.getContentUri("external") val projection = arrayOf( MediaStore.Files.FileColumns.DISPLAY_NAME, MediaStore.Files.FileColumns.SIZE, MediaStore.Files.FileColumns._ID ) val selection = "${MediaStore.Files.FileColumns.MIME_TYPE} = ?" val selectionArgs = arrayOf("application/pdf") val sortOrder = "${MediaStore.Files.FileColumns.DATE_ADDED} DESC" context.contentResolver.query( collection, projection, selection, selectionArgs, sortOrder )?.use { cursor -> val nameIdx = cursor.getColumnIndexOrThrow(MediaStore.Files.FileColumns.DISPLAY_NAME) val sizeIdx = cursor.getColumnIndexOrThrow(MediaStore.Files.FileColumns.SIZE) val idIdx = cursor.getColumnIndexOrThrow(MediaStore.Files.FileColumns._ID) while (cursor.moveToNext()) { val id = cursor.getLong(idIdx) val uri = ContentUris.withAppendedId(collection, id) result.add( PdfItem( name = cursor.getString(nameIdx), uri = uri, size = cursor.getLong(sizeIdx) ) ) } } return result }

关键点说明:MediaStore.Files.getContentUri("external")拿到外部存储的文件集合;selection用 MIME 类型过滤,比按扩展名.pdf匹配更准,因为有些文件扩展名和实际类型不一致;ContentUris.withAppendedId把_ID拼成可直接读取的content://Uri,Android 10+ 用这个 Uri 打开文件流最稳,不要再去拼/storage/emulated/0/...这种绝对路径。

如果你确实需要绝对路径(比如传给某些只认路径的库),可以额外查MediaStore.Files.FileColumns.DATA列,但要清楚这个列在 Android 10+ 上可能返回空或不可读,属于兼容性写法,不推荐作为主路径。

3.2 Cline 接入配置骨架

Cline 是 VS Code 里的编码 Agent,配置走 OpenAI Compatible 模式。在设置里选 Provider 为「OpenAI Compatible」,然后填:

{ "provider": "openai-compatible", "baseUrl": "https://taotoken.net/api", "apiKey": "sk-xxxx", "model": "你的模型名", "temperature": 0.2 }

baseUrl填https://taotoken.net/api,Cline 会自动补/v1/chat/completions。model填你在模型列表里看到的名称。温度调低一点(0.2 左右)是因为处理文件清单这类任务需要稳定输出,不要让它自由发挥。

3.3 Claude Code 接入配置骨架

Claude Code 通过环境变量读取通道配置,在 shell 的~/.zshrc或~/.bashrc里加:

export ANTHROPIC_BASE_URL="https://taotoken.net/api" export ANTHROPIC_API_KEY="sk-xxxx"

改完执行source ~/.zshrc生效。Claude Code 的详细接入说明在文档页 https://taotoken.net/doc?utm_source=taotoken_aicg_blog_end&utm_content=doc&utm_campaign=rewrite ,里面有不同操作系统的完整步骤。如果你用的是 Claude Code 的 Anthropic 原生通道,参考 https://taotoken.net/claudecode-anthropic?utm_source=taotoken_aicg_blog_end&utm_content=claudecode-anthropic&utm_campaign=rewrite 这个页面,它专门讲 Anthropic 协议下的配置差异。

3.4 config.toml 通用骨架

有些工具(比如某些 CLI Agent)用 TOML 配置,骨架如下:

[provider] name = "taotoken" base_url = "https://taotoken.net/api" api_key = "sk-xxxx" model = "你的模型名" [request] timeout = 60 max_retries = 3

timeout给 60 秒,因为 PDF 内容如果较长,模型响应会慢一些;max_retries给 3 次,网络抖动时自动重试。

4. 验证请求:从扫描结果到模型调用

扫描代码写完后,先验证文件清单是否正确。在 Activity 里调用并打印:

val pdfs = scanLocalPdfs(this) Log.d("PDFScan", "共找到 ${pdfs.size} 个 PDF") pdfs.take(5).forEach { Log.d("PDFScan", "${it.name} | ${it.size} bytes | ${it.uri}") }

跑起来看 Logcat,如果数量为 0,先检查权限是否授予、模拟器里是否真的存了 PDF 文件。确认清单没问题后,验证 AI 通道。用 curl 发一个最小请求:

curl https://taotoken.net/api/v1/chat/completions \ -H "Content-Type: application/json" \ -H "Authorization: Bearer sk-xxxx" \ -d '{ "model": "你的模型名", "messages": [ {"role": "user", "content": "回复 OK 两个字母即可"} ] }'

返回里能看到choices[0].message.content是OK,说明通道通了。这一步很关键,很多人配置完直接上复杂任务,报错了分不清是通道问题还是业务问题,先用最小请求把通道验证掉。

通道通了之后,把扫描结果和模型调用串起来。思路是:把 PDF 文件名列表拼成一段文本,发给模型做分类或摘要。比如:

val names = pdfs.joinToString("\n") { it.name } val prompt = "以下是本地 PDF 文件清单,请按主题分类:\n$names" // 把 prompt 通过你的 HTTP 客户端发到 https://taotoken.net/api/v1/chat/completions

如果你要读 PDF 正文内容,需要先把content://Uri 转成字节流,再用 PDF 解析库(如 PdfBox-Android)提取文本,然后把文本作为 message 内容发出去。注意单次请求的 token 上限,长 PDF 要分段。

5. 本篇常见错排查

扫描结果为 0:最常见原因是权限没授予。Android 13+ 上READ_EXTERNAL_STORAGE已失效,要动态申请READ_MEDIA_*系列,或者引导用户去系统设置里开「所有文件访问权限」。另一个原因是模拟器里根本没存 PDF,用adb push推一个进去再测。

Uri 能拿到但打不开:Android 10+ 分区存储下,直接拼绝对路径会失败。用ContentUris.withAppendedId生成的content://Uri 配合contentResolver.openInputStream(uri)读取,不要用File(path)。

Cline 报 401:Key 填错或没带Bearer前缀。检查apiKey字段是不是完整的sk-开头字符串,有没有多余空格。

Claude Code 报连接失败:环境变量没生效。确认ANTHROPIC_BASE_URL拼写正确,改完配置文件后重新开一个终端窗口,或者source一下。如果还是不行,去文档页对照你的系统版本检查。

请求超时:PDF 内容太长导致模型处理慢。把timeout调到 120 秒,或者把 PDF 分段发送。另外确认baseUrl没有多写或少写/v1,不同工具对路径的处理不一样。

模型名报错:填的模型名不在可用列表里。去模型对话页确认当前可用的模型名称,复制粘贴,不要手打。

6. 后续怎么用:把链路固定下来

扫描 + 接入跑通后,建议把配置固化成模板。Android 侧把scanLocalPdfs封装成工具类,AI 侧把 Key 和 Base URL 放到local.properties或环境变量里,不要散落在代码各处。长期做编码类任务的话,Coding Plan 比按量计费更划算,地址在 https://taotoken.net/coding-plan?utm_source=taotoken_aicg_blog_end&utm_content=coding-plan&utm_campaign=rewrite 。需要新建或轮换 Key 时去 API Keys 页面 https://taotoken.net/api-keys?utm_source=taotoken_aicg_blog_end&utm_content=api-keys&utm_campaign=rewrite ,接入细节查文档 https://taotoken.net/doc?utm_source=taotoken_aicg_blog_end&utm_content=doc&utm_campaign=rewrite 。想先试试模型效果,直接去模型对话页 https://taotoken.net/models?utm_source=taotoken_aicg_blog_end&utm_content=models&utm_campaign=rewrite 发一条消息就能验证。

最后留一个我踩过的坑:MediaStore 的查询结果有缓存,新推入的 PDF 文件可能不会立刻出现在列表里。测试时如果发现文件「消失」了,用contentResolver.notifyChange触发一次刷新,或者重启 App 再查。这个细节教程里很少提,但调试时能省不少时间。

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询