尧图网站设计 尧图网站设计YAOTU DESIGN
ARTICLE DETAIL

资讯详情

深耕网站设计与一线实操的经验洞察。

EasyExcel 获取指定行与总行数:TaoToken 统一 Key 通道下的可复制配置与验证

EasyExcel 获取指定行与总行数:TaoToken 统一 Key 通道下的可复制配置与验证 1. EasyExcel 读取时怎么拿到指定行和总行数为什么总有人绕远路EasyExcel 获取指定行与总行数是很多做批量导入、对账、报表解析的同学都会卡一下的点。EasyExcel 本身是一个流式读取 Excel 的工具它的设计目标是「边读边处理、不把整个文件塞进内存」所以它天生不会像 POI 那样先给你一个ListListString让你随便size()。很多人第一次用的时候会下意识去找getRowCount()、getSheet().getLastRowNum()这类 API结果翻遍文档发现没有于是开始写各种监听器、计数器、临时集合代码越写越乱。我先把结论摆出来EasyExcel 没有直接返回「总行数」的现成方法但获取总行数并不难关键看你用哪种读取模式。如果你用的是同步doReadSync那返回的List的size()就是数据行数如果你用的是监听器AnalysisEventListener那就在invoke里自增一个计数器在doAfterAllAnalysed里拿到最终值。至于「指定行」本质是读取时按行号过滤或者读完之后按索引取取决于你是想「只读第 N 行」还是「读完之后取第 N 行」。这里有个容易被忽略的前提EasyExcel 的「行」和 Excel 界面上的「行号」不是一回事。表头默认占一行invoke回调里拿到的是数据行不包含表头。所以你在界面上看到第 5 行如果表头是第 1 行那它在数据里其实是第 4 条。这个偏移量如果没对齐后面统计总行数、取指定行都会差一位排查起来很费时间。再说到场景。实际项目里读取 Excel 往往不是孤立动作而是整条数据链路的一环解析完 Excel 之后可能要把数据送去大模型做字段补全、做语义校验、做批量翻译或者调用某个 API 做比对。这时候如果你还在用零散的、每个项目一套的 Key 管理方式配置会非常碎。我这次把 EasyExcel 的读取和 TaoToken 统一 Key 通道放在一起讲就是因为这两件事经常同时出现Excel 解析负责「把数据拿出来」统一 Key 通道负责「把数据送出去」。把读取配置和通道配置都固定成可复制的模板后面换项目、换模型、换环境都省事。下面我会按「先讲读取本身再讲通道配置然后给可复制代码接着验证最后排错」的顺序展开。你可以只取自己需要的部分但建议至少把第 3 节的配置片段和第 4 节的验证跑一遍因为很多问题不是出在 EasyExcel而是出在依赖版本和通道参数上。2. TaoToken 统一 Key 通道前置准备Base URL、Key 与模型 ID 三件套在写 EasyExcel 代码之前先把「通道」这件事说清楚。所谓统一 Key 通道就是把原本散落在各个项目、各个环境变量里的 API Key、Base URL、模型名收敛成一套可复用的配置。TaoToken 在这里扮演的角色是统一入口你拿到一个 Key配好 Base URL指定模型 ID就能在代码里发起请求。官网入口是 https://taotoken.net/?utm_sourcetaotoken_aicg_blog_endutm_mediumcsdnutm_campaignrewriteutm_content API 地址是 https://taotoken.net/api 注意 API 地址后面不加 UTM 参数保持干净。前置准备其实就三样东西我习惯叫它「三件套」第一件是 Base URL。它是请求的根地址所有接口路径都拼在它后面。写配置的时候一定要写全别漏掉/api这一段很多人 404 就是因为只写了域名。第二件是 API Key。它相当于通行证放在请求头的Authorization里格式通常是Bearer sk-xxxx。Key 不要硬编码进 Git 仓库用环境变量或者本地配置文件承载。第三件是 Model ID。不同模型的名字不一样你要用哪个就填哪个填错了会报模型不存在或者 400。Model ID 是大小写敏感的复制的时候别手改。把这三件套固定下来之后EasyExcel 读取出来的每一行数据都可以用同一套配置去调用模型不用每换一个功能就改一次 Key。我试过在多个小工具之间共用同一份配置切换成本几乎为零。这里给一个配置文件的示例你可以放在项目根目录也可以放在~/.config下。JSON 格式如下路径按你自己的习惯来但字段名建议保持一致方便复用{ base_url: https://taotoken.net/api, api_key: sk-你的Key, model_id: 你的模型ID, timeout_seconds: 60 }如果你更喜欢 TOML等价写法是这样base_url https://taotoken.net/api api_key sk-你的Key model_id 你的模型ID timeout_seconds 60注意base_url结尾不要多加斜杠也不要少写/api。api_key用你自己的别照抄示例。model_id填你实际要用的。timeout_seconds是超时时间批量处理 Excel 的时候建议给足别用默认的几秒否则数据一多就超时。如果你用的是 Claude Code 这类工具配置会落在settings.json里字段名和上面类似Base URL、Key、Model ID 三件套一个都不能少。Cline 的 MCP 配置也是同样的逻辑把这三件套填进对应的配置块即可。Codex 的auth.json同理Base URL 和 Key 要对齐。不管载体是什么核心永远是这三件套别在格式上纠结太久。3. 可复制配置EasyExcel 读取指定行与总行数的完整代码这一节是重点我直接给可复制的代码。先声明依赖Maven 里加 EasyExcel版本用较新的稳定版即可dependency groupIdcom.alibaba/groupId artifactIdeasyexcel/artifactId version3.3.4/version /dependency如果你用 Gradleimplementation com.alibaba:easyexcel:3.3.4接下来分两种模式讲。第一种是同步读取适合文件不大、你想一次性拿到所有行的场景。第二种是监听器读取适合文件大、你想边读边处理、并且需要精确控制行号的场景。先看同步读取。这种方式最直观doReadSync返回一个Listsize()就是数据行数取指定行就是list.get(index)import com.alibaba.excel.EasyExcel; import java.util.List; import java.util.Map; public class SyncReadDemo { public static void main(String[] args) { String fileName data.xlsx; ListMapInteger, String rows EasyExcel.read(fileName) .sheet() .doReadSync(); int total rows.size(); System.out.println(总行数(不含表头): total); int targetIndex 2; if (targetIndex 0 targetIndex total) { MapInteger, String row rows.get(targetIndex); System.out.println(第 (targetIndex 1) 条数据: row); } else { System.out.println(指定行超出范围); } } }这段代码里doReadSync不带实体类返回的是MapInteger, Stringkey 是列索引value 是单元格字符串。如果你有实体类把MapInteger, String换成你的类即可比如ListUserExcelRow。总行数就是rows.size()注意它不含表头。指定行用rows.get(targetIndex)索引从 0 开始。再看监听器模式。这种方式更贴近 EasyExcel 的设计初衷内存占用低而且能在读取过程中就拿到行号import com.alibaba.excel.context.AnalysisContext; import com.alibaba.excel.read.listener.ReadListener; import com.alibaba.excel.EasyExcel; import java.util.Map; public class ListenerReadDemo { static class RowCounterListener implements ReadListenerMapInteger, String { private int count 0; private MapInteger, String targetRow; private final int targetIndex; public RowCounterListener(int targetIndex) { this.targetIndex targetIndex; } Override public void invoke(MapInteger, String data, AnalysisContext context) { if (count targetIndex) { targetRow data; } count; } Override public void doAfterAllAnalysed(AnalysisContext context) { System.out.println(总行数(不含表头): count); if (targetRow ! null) { System.out.println(指定行数据: targetRow); } else { System.out.println(指定行超出范围); } } } public static void main(String[] args) { String fileName data.xlsx; EasyExcel.read(fileName, new RowCounterListener(2)) .sheet() .doRead(); } }这里的关键点invoke每读一行调用一次count自增doAfterAllAnalysed在所有行读完后调用此时count就是总行数。指定行在invoke里判断count targetIndex时保存下来。注意count是在判断之后自增的所以targetIndex从 0 开始对应第一条数据。如果你要按 Excel 界面行号来取记得加上表头偏移。比如表头占 1 行界面第 5 行对应数据索引 35 - 1 - 1 3。这个换算写个注释免得下次自己都忘了。再补一个「只读指定行、不读全部」的优化写法。如果你明确只要第 N 行可以在invoke里提前判断但 EasyExcel 没有「跳过前 N 行」的原生参数所以要么全读一遍要么用headRowNumber控制表头行数再配合计数。headRowNumber的用法EasyExcel.read(fileName, new RowCounterListener(2)) .sheet() .headRowNumber(1) .doRead();headRowNumber(1)表示表头占 1 行数据从第 2 行开始。如果你的表头有多行改成对应数字。这个参数不影响invoke里的计数逻辑invoke拿到的始终是数据行。最后把通道配置和读取串起来。假设你读出来的每一行要调用模型做处理可以这样组织import java.net.URI; import java.net.http.HttpClient; import java.net.http.HttpRequest; import java.net.http.HttpResponse; public class ChannelCaller { private final String baseUrl; private final String apiKey; private final String modelId; public ChannelCaller(String baseUrl, String apiKey, String modelId) { this.baseUrl baseUrl; this.apiKey apiKey; this.modelId modelId; } public String call(String prompt) throws Exception { String body {\model\:\ modelId \,\messages\:[{\role\:\user\,\content\:\ prompt \}]}; HttpRequest request HttpRequest.newBuilder() .uri(URI.create(baseUrl /v1/chat/completions)) .header(Authorization, Bearer apiKey) .header(Content-Type, application/json) .POST(HttpRequest.BodyPublishers.ofString(body)) .build(); HttpClient client HttpClient.newHttpClient(); HttpResponseString response client.send(request, HttpResponse.BodyHandlers.ofString()); return response.body(); } }这段代码里baseUrl就是https://taotoken.net/apiapiKey和modelId来自你的配置。路径/v1/chat/completions是常见的对话接口路径具体以文档为准。把读取和调用分开职责清晰后面换模型只改配置不改读取逻辑。4. 验证请求与成功结果怎么确认行数和指定行都对代码写完不算完得验证。验证分两层第一层验证 EasyExcel 读取的行数和指定行是否正确第二层验证通道请求是否通。先准备一个测试 Excel我建议手动造一个别用生产文件免得数据敏感。造一个 5 行数据的表表头一行数据五行第一列写 1 到 5第二列写 A 到 E。保存为data.xlsx。跑同步读取的代码预期输出总行数(不含表头): 5 第 3 条数据: {03, 1C}注意targetIndex 2对应的是第三条数据因为索引从 0 开始。如果你输出的是{02, 1B}说明你的索引理解偏了一位检查targetIndex的取值。跑监听器模式的代码预期输出一样。如果两个模式输出不一致优先怀疑表头行数设置。headRowNumber默认是 1如果你的表头有两行invoke会把第二行表头也当成数据总行数就会多 1。再验证指定行边界。把targetIndex改成 10超出范围预期输出「指定行超出范围」而不是抛异常。如果你代码里直接rows.get(10)没做判断会抛IndexOutOfBoundsException这就是没做边界检查的坑。第二层验证通道。写一个最小请求确认 Base URL、Key、Model ID 三件套都对curl -X POST https://taotoken.net/api/v1/chat/completions \ -H Authorization: Bearer sk-你的Key \ -H Content-Type: application/json \ -d {model:你的模型ID,messages:[{role:user,content:你好}]}预期返回一个 JSON里面有choices字段choices[0].message.content是模型的回复。如果返回 401说明 Key 不对或者没带上如果返回 404说明 Base URL 或路径不对如果返回 400多半是 Model ID 写错或者请求体格式不对。成功的结果长这样字段可能因模型而异{ choices: [ { message: { role: assistant, content: 你好有什么可以帮你 } } ] }看到choices里有内容就说明通道通了。这时候再把 EasyExcel 读出来的行拼成 prompt 发过去整条链路就打通了。你可以先拿第一行数据试确认没问题再批量。验证的时候有个小技巧把总行数和指定行先打印出来再发请求这样一旦结果不对你能快速判断是读取错了还是请求错了。别一上来就批量跑出了问题不好定位。5. 本篇常见错排查401、local proxy failed、reading choices、OAuth 对照这一节我把常见的报错和现象列出来对照着排查。这些错我基本都遇到过写下来省得你重复踩。第一个401 Unauthorized。这个最直接Key 的问题。检查三件事Key 有没有带Bearer前缀Key 有没有复制完整前后别多空格Key 有没有过期。如果你把 Key 放在环境变量里确认程序真的读到了别是空字符串。还有一种情况是配置文件里写了 Key但代码读的是另一个路径的配置导致实际用的是旧 Key。第二个local proxy failed。这个报错通常出现在网络层意思是本地请求没能发出去。先确认你的 Base URL 写对了https://taotoken.net/api别写成别的。再确认本机网络能正常访问外网DNS 解析正常。如果你在公司内网可能有网络策略限制换一个网络环境试试。注意这里不要引入任何网络加速工具就用正常网络排查。第三个reading choices 相关报错。这个一般出现在解析响应的时候比如你代码里写死了response.choices[0]但实际返回的结构不一样或者返回的是错误信息没有choices字段。排查方法先把原始响应体打印出来别急着解析。看到原始 JSON 你就知道是成功了还是报错了。如果响应里有error字段先处理错误别硬解析choices。第四个OAuth 相关报错。如果你用的是 Claude Code 这类带 OAuth 流程的工具报 OAuth 错通常是配置里的认证方式没对齐。检查settings.json里的字段Base URL、Key、Model ID 三件套是否齐全认证类型是否选对。有些工具需要你先完成一次授权授权没走完就直接调用会报错。按工具的文档把授权流程走一遍再回来调用。第五个行数对不上。这个不算报错但很常见。表现是总行数比预期多 1 或少 1。多 1 通常是表头被算进去了检查headRowNumber少 1 通常是最后一行空行被跳过或者文件本身少一行。用 Excel 打开文件看最后一行是不是空的。EasyExcel 对空行的处理有默认行为必要时在invoke里判断数据是否为空再计数。第六个指定行取到的是空。这个多半是索引偏移问题。再强调一遍invoke里的计数不含表头doReadSync返回的 List 也不含表头。你按界面行号取的时候记得减掉表头行数再减 1。写个工具方法做换算别每次手算。第七个依赖冲突。EasyExcel 依赖 POI如果你的项目里已经有别的 POI 版本可能冲突表现是NoSuchMethodError或ClassNotFoundException。用mvn dependency:tree看一下 POI 的版本统一到一个兼容版本。EasyExcel 3.x 一般配 POI 5.x别混用太老的版本。第八个大文件内存溢出。如果你用同步读取读一个几十万行的文件doReadSync会把所有行放内存容易 OOM。这种场景必须用监听器模式边读边处理别攒着。监听器模式的内存占用和文件大小基本无关只和你单行处理逻辑有关。排查的时候记住一个原则先确认读取对不对再确认请求通不通最后才看业务逻辑。顺序反了问题会互相掩盖。6. 把读取和通道固定成模板后面直接复用走到这里EasyExcel 获取指定行与总行数的核心已经讲完了。同步读取用doReadSync拿 Listsize()是总行数get(index)取指定行监听器模式在invoke里自增计数doAfterAllAnalysed里拿总数指定行在invoke里按计数保存。表头偏移是唯一需要留意的细节记住「数据索引 界面行号 - 表头行数 - 1」。通道这边Base URL 用https://taotoken.net/apiKey 和 Model ID 按你的实际配置填三件套固定下来之后读取和调用就解耦了。读取负责把 Excel 变成结构化数据通道负责把数据送出去两边各自独立换哪个都不影响另一个。我建议你把第 3 节的配置片段和读取代码存成一个模板项目下次遇到类似需求直接改字段名和路径不用从头写。模板里把总行数、指定行、边界检查、通道调用都封装好用的时候只传文件名和目标索引。这样既省时间也避免每次都在表头偏移上栽跟头。如果你还想验证模型返回可以到模型对话页面直接试一条请求确认 Key 和模型 ID 没问题再写进代码。长期做编码和 Agent 类任务的话Coding Plan 更适合把配置固定下来省得每次重新配。接入文档里有完整的参数说明遇到不确定的字段先去文档核对别靠猜。
返回列表