一、问题现象
把一个第三方 jar 上传到公司 Nexus 私服后,本地编译一直报错:
[ERROR] The following artifacts could not be resolved:
com.yonyou.iuap:iuap-ip-openapi-sdk:jar:8.2.100-RELEASE (present, but unavailable):
Could not find artifact com.yonyou.iuap:iuap-ip-openapi-sdk:jar:8.2.100-RELEASE
in nexus-public (http://maven.puyiyun.com/repository/maven-public/)
诡异的地方在于:浏览器里能正常下载这个 jar,链接是通的,信息也完全一致。
http://maven.puyiyun.com/repository/maven-releases/com/yonyou/iuap/iuap-ip-openapi-sdk/8.2.100-RELEASE/iuap-ip-openapi-sdk-8.2.100-RELEASE-RELEASE.jar

二、先排除掉的那些”常见嫌疑”
按常规思路排查了一圈,全部排除:
| 排查项 | 结论 |
|---|---|
| Nexus 上文件是否存在 | 存在,curl -I 返回 200 |
settings.xml 的 mirror / profile / server 认证 | 正确,同仓库其他依赖都能正常拉取 |
本地缓存 .lastUpdated / _remote.repositories | 已删除对应目录并 -U 强制更新,无效 |
| release / snapshot 类型不匹配 | 是 RELEASE,无问题 |
到这里基本可以确定:不是权限、不是网络、不是缓存、不是配置,而是”坐标”和”文件名”对不上。
三、根因:jar 文件名多了一个 classifier
把 Maven 要找的文件和仓库里实际存在的文件并排看,一目了然:
| 文件名 | |
|---|---|
Maven 按坐标 8.2.100-RELEASE 要找的 | iuap-ip-openapi-sdk-8.2.100-RELEASE.jar |
| Nexus 上真实存在的 | iuap-ip-openapi-sdk-8.2.100-RELEASE-RELEASE.jar |
按 Maven 的命名规范:
{artifactId}-{version}[-{classifier}].{extension}
version 是 8.2.100-RELEASE,那么末尾多出来的那段 RELEASE 就被解析成了一个 classifier。也就是说,仓库里只有”带 classifier 的 jar”,没有主 jar。
这也解释了报错里那句很特别的措辞 —— present, but unavailable:Maven 在仓库里找到了该 artifact 的目录和 pom(所以是 present),但按无 classifier 的坐标请求主 jar 时找不到文件(所以 unavailable)。
小结:
Could not find artifact+present, but unavailable这个组合,八成是坐标与文件名不匹配,而不是仓库真没有。
四、解决方案
方案 A(应急):在 pom 里补上 classifier
如果只是想先把编译跑通、暂时不方便动仓库:
<dependency>
<groupId>com.yonyou.iuap</groupId>
<artifactId>iuap-ip-openapi-sdk</artifactId>
<version>8.2.100-RELEASE</version>
<classifier>RELEASE</classifier>
</dependency>
缺点很明显:这是”迁就错误命名”,团队里其他人拉取时也得跟着加 classifier,而且传递依赖可能仍解析不到。只适合救火。
方案 B(治本,我最终采用的):重命名后重新上传
关键动作只有一个——把本地文件名改成和坐标完全一致:
iuap-ip-openapi-sdk-8.2.100-RELEASE.jar
iuap-ip-openapi-sdk-8.2.100-RELEASE.pom
即去掉末尾多出来的那个 -RELEASE,然后重新上传:
- Repository 选
maven-releases - Group / Artifact / Version 填
com.yonyou.iuap/iuap-ip-openapi-sdk/8.2.100-RELEASE - Classifier 留空,Extension 分别是
jar和pom - 不要勾选 “Generate a POM file with these coordinates”(已有真实 pom,勾了会被空 pom 覆盖,传递依赖全丢)

上传后清掉本地该版本目录,强制拉取:
rm -rf ~/.m2/repository/com/yonyou/iuap/iuap-ip-openapi-sdk/8.2.100-RELEASE
mvn clean compile -U
编译通过,问题解决。
五、一个意外发现:pom 用 .xml 还是 .pom,效果不一样
重新上传过程中发现一个很有意思的现象:内容完全相同的 pom 文件,仅后缀不同,结果就不同。
| pom 文件名 | Extension 填 pom、Classifier 留空时的结果 |
|---|---|
xxx.xml | jar 落盘为 ...-8.2.100-RELEASE-RELEASE.jar(双 RELEASE) |
xxx.pom | jar 落盘为 ...-8.2.100-RELEASE.jar(正确) |
原因推测在于 Nexus 上传 maven2 组件时的坐标来源优先级:
pom 里解析出的 GAV > 表单手填的 GAV
Components REST API 文档里,maven2.groupId / artifactId / version 都标注为 “Yes, unless a POM asset is included in the upload” —— 只要上传里带了可识别的 pom,Nexus 就以 pom 内容为准,并按规范重命名整个组件的资产。
- 命名为
.pom时:被识别为 pom 资产 → 解析出权威坐标 → 按{artifactId}-{version}.jar规范化重命名 → 正确 - 命名为
.xml时:很可能未被识别为 pom 资产(判定依据偏向文件名后缀,而非 Extension 输入框里手填的字符串)→ 缺少权威坐标 → 只能照抄本地文件名 → 双 RELEASE 被原样带进仓库
顺带解释了之前另一个怪现象:上传时 Classifier 会被自动填成 RELEASE。同样是这套逻辑——Nexus 拿第一个文件的文件名去猜 artifactId-version-classifier.ext,看到 ...-8.2.100-RELEASE-RELEASE.jar 就把末尾那段当成 classifier 填进去了。手动清空只是掩盖症状,根子仍是源文件名不合规。
⚠️ 这个”靠文件名后缀识别 pom”的细节在不同 3.x 小版本实现上有差异,不要当成稳定契约依赖,只作为”为什么会出现不一致”的解释。
六、经验沉淀(Checklist)
以后往 Nexus 传第三方 jar,按这个顺序走基本不会翻车:
- 先规范文件名:
{artifactId}-{version}.jar/{artifactId}-{version}.pom,pom 一律用.pom后缀,不要用.xml - 核对 pom 内部坐标与表单填写一致,否则 metadata 会不匹配
- 不要勾 Generate POM(除非你确实没有 pom)
- 上传后立刻用 curl 校验,两个都 200 才算可用:
curl -I ".../8.2.100-RELEASE/iuap-ip-openapi-sdk-8.2.100-RELEASE.jar"
curl -I ".../8.2.100-RELEASE/iuap-ip-openapi-sdk-8.2.100-RELEASE.pom"
- 删掉仓库里遗留的错误命名 asset,避免后人踩同一个坑
- 本地清缓存 +
-U验证
七、更推荐的做法:用命令行上传
网页上传的坑在于文件名会影响结果。mvn deploy:deploy-file 则完全不看你本地文件叫什么,一律按 GAV 生成路径和文件名,还能批量处理:
mvn deploy:deploy-file \
-DgroupId=com.yonyou.iuap \
-DartifactId=iuap-ip-openapi-sdk \
-Dversion=8.2.100-RELEASE \
-Dpackaging=jar \
-Dfile=iuap-ip-openapi-sdk-8.2.100-RELEASE.jar \
-DpomFile=iuap-ip-openapi-sdk-8.2.100-RELEASE.pom \
-DrepositoryId=nexus-releases \
-Durl=http://maven.puyiyun.com/repository/maven-releases/
若确实没有 pom,可用 -DgeneratePom=true 让 Maven 生成,但生成的 pom 不含依赖声明,传递依赖会丢失,能拿到原始 pom 就尽量用 -DpomFile。
八、一句话总结
不是仓库没有、也不是配置问题,而是上传时文件名多带了一段 RELEASE,被 Maven 解析成了 classifier,导致主 jar 缺失。 把本地 jar/pom 重命名为与坐标完全一致后重新上传即可;pom 文件后缀用 .pom 而不是 .xml,或直接改用 mvn deploy:deploy-file,能从根本上避免这类”文件名决定命运”的坑。



