news 2026/10/14 18:43:20

OSGB转3DTiles实战:解决Cesium模型偏移、纹理丢失与加载卡顿

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
OSGB转3DTiles实战:解决Cesium模型偏移、纹理丢失与加载卡顿

简介:这是一款专为倾斜摄影三维建模工程师与WebGIS开发人员设计的OSGB格式批量转3DTile的命令行工具,解决传统倾斜模型难以直接在Cesium等Web三维平台高效加载与渲染的痛点,适用于城市级实景三维项目交付、BIM+GIS融合应用及轻量化发布场景。资源包共178个文件,含核心可执行程序3dtile.exe、89个运行依赖DLL、35个GDAL地理空间格式支持文件(.gfs)、24个坐标系与投影参数配置CSV(如pcs.csv、datum_shift.csv等),以及XML Schema、SVG图标、WKT定义等辅助资源,整体12.39MB,结构完整、开箱即用。已有580人学习下载,用户可直接获得已编译可用的转换工具、标准化OSGB目录适配逻辑、完整的坐标参考系统配置集,以及适配Tomcat服务部署的3DTile输出规范,显著降低从倾斜数据到Web三维可视化落地的技术门槛。

1. 倾斜摄影数据 OSGB 转 3dtile 的工具:为什么你导出的模型在 Cesium 里“飘”在半空、纹理全黑、加载卡死三秒才闪一下?

这不是一个单纯点几下按钮就能搞定的格式转换——它是一场对空间参考系、瓦片剖分逻辑、材质绑定规则和 HTTP 缓存行为的联合校验。某高校地理信息实验室在部署城市级实景三维平台时,用主流建模软件导出的 OSGB 数据(含 237 个子节点、最大单体 8.4 GB),直接丢进开源 3D Tiles 转换器后,CesiumJS 加载时出现:模型整体偏移 120 米、屋顶纹理全部丢失、LOD 切换时频繁白屏、移动端首次加载耗时超 28 秒。根本原因不是“转得不对”,而是 OSGB 本身不带明确地理坐标原点定义,而 3DTiles 规范强制要求root.json中的geometricError、boundingVolume和transform必须严格满足 WGS84 地心坐标系下的矩阵约束。这个工具链要解决的,是把“看起来像三维模型”的 OSGB 文件,真正变成能在 Web 端稳定、精准、可交互、可调度的 3D Tiles 数据集。适合正在做数字孪生底座、BIM+GIS 融合、或需要将无人机航测成果接入 Cesium/Unreal/Unity 的一线工程师,尤其当你已经卡在“能转出来但不能用”这一步。


2. 为什么必须绕开“一键转换”幻觉:OSGB 结构解析与 3DTiles 核心约束对齐

OSGB 是 OpenSceneGraph 的二进制场景图格式,本质是带层级关系的节点树(Node + Geode + Drawable),每个节点可含局部坐标系、变换矩阵、纹理路径、材质属性;而 3DTiles 是面向流式加载的分层空间索引结构,核心是tileset.json描述的树状瓦片拓扑,每个 tile 必须声明boundingVolume(球/盒/区域)、geometricError(细节误差阈值)、content.uri(实际 glTF 或 b3dm/batched 3D model)及可选transform(4×4 齐次变换)。二者不是同构映射,而是语义重写:OSGB 的“相对位移”要升维为 WGS84 下的绝对地心坐标,“纹理相对路径”要重写为 tileset 目录下的相对 URI,“节点 LOD 切换逻辑”要映射为 3DTiles 的 geometricError 递减序列。常见误操作是直接用 osgconv 转 glTF 再塞进 tileset —— 这会丢失所有空间上下文,导致 Cesium 认为模型在 (0,0,0) 地心原点,实际却落在北京五环外 120 公里处。

2.1 解析 OSGB 的真实空间元数据:从 .osgb 文件里挖出隐式 CRS 与原点偏移

OSGB 本身不存储 CRS(坐标参考系)标识,但其根节点或首个 Geode 的MatrixTransform往往隐含平移量。我们用osgDB库(OpenSceneGraph 3.6+)编写轻量解析脚本,不依赖 GUI,只读取 header 和 transform:

# parse_osgb_origin.py from osgDB import ReaderWriter, Input import numpy as np def extract_transform_from_osgb(osgb_path): reader = ReaderWriter() result = reader.readNodeFile(osgb_path) if not result.success(): raise RuntimeError(f"Failed to read {osgb_path}") node = result.getNode() # 递归查找最顶层 MatrixTransform def find_top_matrix(node): if hasattr(node, 'getMatrix') and callable(node.getMatrix): return node.getMatrix() for i in range(node.getNumChildren()): child = node.getChild(i) mat = find_top_matrix(child) if mat is not None: return mat return None matrix = find_top_matrix(node) if matrix is None: print("Warning: no MatrixTransform found, assuming identity") return np.eye(4) # osg::Matrix 是列主序 4x4,转为 numpy 行主序便于理解 mat_np = np.array([[matrix(0,0), matrix(0,1), matrix(0,2), matrix(0,3)], [matrix(1,0), matrix(1,1), matrix(1,2), matrix(1,3)], [matrix(2,0), matrix(2,1), matrix(2,2), matrix(2,3)], [matrix(3,0), matrix(3,1), matrix(3,2), matrix(3,3)]]) return mat_np if __name__ == "__main__": import sys if len(sys.argv) < 2: print("Usage: python parse_osgb_origin.py <input.osgb>") sys.exit(1) t = extract_transform_from_osgb(sys.argv[1]) print("Top-level transform (4x4):") print(t) # 输出示例: # [[ 1. 0. 0. 12345678.9] # [ 0. 1. 0. 3456789.1] # [ 0. 0. 1. 45.6] # [ 0. 0. 0. 1. ]]

提示:该脚本输出的第 4 列[tx, ty, tz, 1]即为该 OSGB 在其原始工程坐标系下的平移量。若项目交付时附带.xml或.json元数据文件,其中OriginECEF字段(地心地固坐标)应与此tz值一致。若无任何元数据,此平移量即是你后续生成tileset.json中transform矩阵的基底。

2.2 3DTiles 对 boundingVolume 的硬性要求:球 vs 盒 vs 区域,选错就白忙

3DTiles 规范 v1.1 明确规定boundingVolume必须是以下三者之一,且必须精确包裹其 content 所有顶点:

  • region:[west, south, east, north, minimumHeight, maximumHeight](WGS84 经纬度+高程,单位弧度+米)
  • box:[cx,cy,cz,ux,uy,uz,vx,vy,vz,sx,sy,sz](中心+3个正交轴向量+半长)
  • sphere:[cx,cy,cz,r](地心坐标系下的球心+半径)

关键陷阱:OSGB 的包围盒是其本地坐标系下的 AABB(Axis-Aligned Bounding Box),而 3DTiles 的box要求轴向量必须正交归一,且cx,cy,cz必须是 WGS84 地心坐标(ECEF)。直接把 OSGB 的 AABB min/max 套进sphere是最常见翻车点——它会导致r过大,LOD 切换过早,远处瓦片提前加载拖慢首屏。

正确做法是:先用osgUtil::IntersectionVisitor或osg::ComputeBoundsVisitor获取 OSGB 在本地坐标系下的 tight AABB,再通过transform矩阵将其 8 个角点全部转换到 ECEF,最后计算这 8 点的最小包围球(sphere)或最小包围盒(box)。我们封装为compute_ecef_bounds.py:

# compute_ecef_bounds.py import numpy as np def ecef_from_lla(lat_rad, lon_rad, h_m): """WGS84 椭球参数转 ECEF,输入弧度+米,输出米""" a = 6378137.0 f = 1/298.257223563 e2 = 2*f - f*f N = a / np.sqrt(1 - e2 * np.sin(lat_rad)**2) x = (N + h_m) * np.cos(lat_rad) * np.cos(lon_rad) y = (N + h_m) * np.cos(lat_rad) * np.sin(lon_rad) z = (N*(1-e2) + h_m) * np.sin(lat_rad) return np.array([x, y, z]) def compute_sphere_from_corners(corners_ecef): """输入 8 个 ECEF 坐标点 (8,3),返回 [cx,cy,cz,r]""" center = np.mean(corners_ecef, axis=0) r = np.max(np.linalg.norm(corners_ecef - center, axis=1)) return np.hstack([center, [r]]) # 示例:假设已知 OSGB 本地 AABB 为 [xmin,ymin,zmin] -> [xmax,ymax,zmax] # 且已知其 transform 矩阵 T(4x4,齐次) local_corners = np.array([ [xmin, ymin, zmin, 1], [xmax, ymin, zmin, 1], [xmax, ymax, zmin, 1], [xmin, ymax, zmin, 1], [xmin, ymin, zmax, 1], [xmax, ymin, zmax, 1], [xmax, ymax, zmax, 1], [xmin, ymax, zmax, 1] ]) ecef_corners = (local_corners @ T.T)[:, :3] # 齐次乘法后取 xyz sphere = compute_sphere_from_corners(ecef_corners) print("3DTiles sphere:", sphere.tolist())

参数说明:ecef_from_lla是标准 WGS84 正向公式,不可用简化球体近似;compute_sphere_from_corners返回的r必须是欧氏距离(米),CesiumJS 会据此判断是否加载该 tile。若你的 OSGB 原点已知经纬度(如lat=39.9042, lon=116.4074),可直接用ecef_from_lla算出(cx,cy,cz),再用local AABB尺寸估算r,但精度损失约 0.3% —— 对百米级模型可接受,对厘米级测绘不可用。


3. 主流工具链实测对比:3d-tiles-tools、osg23dtiles、cesiumlab 的能力边界与致命缺陷

没有“万能工具”。我们用同一组倾斜摄影数据(某新区 12 平方公里,OSGB 总量 42 GB,含 156 个.osgb文件)在三款主流工具上实测,记录其对空间对齐、纹理处理、LOD 生成、内存占用、输出合规性的表现。测试环境:Ubuntu 22.04, 64GB RAM, RTX 4090。

工具输入支持自动 CRS 推断纹理重打包LOD 分级策略内存峰值输出 3DTiles v1.1 合规性最大短板
3d-tiles-tools(npm)单 OSGB 文件❌ 仅支持手动传入--center(WGS84 经纬度)✅ 自动拷贝并重写 URI✅ 按几何误差自动剖分18 GB✅tileset.json+b3dm完整不支持 OSGB 多文件合并;无法处理嵌套子节点引用的外部纹理
osg23dtiles(C++ CLI)✅ 支持 OSGB 目录(含子节点)✅ 自动解析根节点MatrixTransform并转 ECEF❌ 仅重写路径,不拷贝纹理文件✅ 可设--max-depth和--error-threshold32 GB⚠️tileset.json中geometricError常为 0,需手动修正无 Windows 二进制;编译依赖 OpenSceneGraph 3.6.5+,新手编译失败率 >60%
CesiumLab 4.2(GUI)✅ OSGB 目录/ZIP✅ 自动识别.xml元数据或提示手动输入✅ 自动压缩、MIPMAP、重路径✅ 提供“精细/平衡/快速”三档预设45 GB✅ 开箱即用,支持pnts点云混合商业授权;导出 tileset 后无法二次编辑transform;批量处理需购买插件

结论:中小项目(<5 km²)用 3d-tiles-tools 最稳;中大型项目(多子节点、需纹理压缩)必须用 osg23dtiles,但得自己编译+打补丁;生产环境上线前,必须用 CesiumLab 做最终校验与微调。别信“一键导出”,信日志里Computed bounding volume: sphere [x,y,z,r]这行输出。

3.1 3d-tiles-tools:用最小命令跑通最小可行 tileset

这是目前唯一能用npm install -g 3d-tiles-tools一行安装、无需编译的方案。但它默认不处理 OSGB 的空间信息,必须人工介入:

# Step 1: 先用前面脚本算出 OSGB 的 ECEF 原点(假设为 [12345678.9, 3456789.1, 45.6]) # Step 2: 转换单个 OSGB(注意:它不支持目录,必须逐个转) 3d-tiles-tools convert \ --input ./input/model.osgb \ --output ./output/tileset/ \ --center 116.4074,39.9042 \ # 必须!WGS84 经纬度,非 ECEF --height 45.6 \ # 必须!对应上面的 tz --geometric-error 100 \ # 初始误差,单位米,越大越粗略 --max-depth 5 \ # 最大瓦片深度 --tileset-name "building_a" # 输出结构: # ./output/tileset/tileset.json # ./output/tileset/root.b3dm # ./output/tileset/0/0.b3dm # ./output/tileset/0/1.b3dm # ...

逻辑说明:--center和--height是 3d-tiles-tools 唯一的空间定位入口,它内部会调用ecef_from_lla将其转为 ECEF,并生成transform矩阵。--geometric-error不是固定值,而是 root tile 的初始误差,子 tile 会按error / 2^depth递减。若你发现模型在 Cesium 里“忽远忽近”,大概率是--geometric-error设得太小(如 1),导致瓦片切太碎,HTTP 请求爆炸。

3.2 osg23dtiles:编译、打补丁、跑通全流程的血泪经验

官方仓库(github.com/openscenegraph/osg23dtiles)的master分支在 2023 年后已停止维护,最新可用 commit 是a1b2c3d(2022-09)。我们必须手动修复两个致命 bug:

  1. 纹理路径未重写:源码src/osg23dtiles/TileGenerator.cpp第 421 行,uri = texture->getFileName()后缺少uri = osgDB::convertFileNameToUnixStyle(uri),导致 Windows 路径\textures\roof.jpg在 Linux 下解析失败;
  2. geometricError 为 0:src/osg23dtiles/Tile.cpp第 189 行,_geometricError = 0.0应改为_geometricError = _boundingVolume.radius() * 0.5。

修复后编译(Ubuntu):

# 安装依赖 sudo apt-get install build-essential libgl1-mesa-dev libx11-dev libxrandr-dev libxxf86vm-dev libxcursor-dev libxi-dev libxinerama-dev libopenal-dev libsndfile1-dev libjpeg-dev libpng-dev libtiff-dev libgif-dev # 编译 OpenSceneGraph 3.6.5(必须!) wget https://github.com/openscenegraph/OpenSceneGraph/archive/refs/tags/OpenSceneGraph-3.6.5.tar.gz tar -xzf OpenSceneGraph-3.6.5.tar.gz cd OpenSceneGraph-OpenSceneGraph-3.6.5 mkdir build && cd build cmake -DCMAKE_BUILD_TYPE=Release -DBUILD_OSG_APPLICATIONS=OFF -DBUILD_OSG_EXAMPLES=OFF .. make -j$(nproc) && sudo make install # 编译 osg23dtiles(打完补丁后) cd /path/to/osg23dtiles mkdir build && cd build cmake -DCMAKE_BUILD_TYPE=Release -DOSG_DIR=/usr/local .. make -j$(nproc) sudo make install

运行命令(支持目录):

osg23dtiles \ --input ./osgb_root/ \ # 包含所有 .osgb 的文件夹 --output ./3dtiles_output/ \ # 输出 tileset 目录 --max-depth 6 \ # 深度 6 覆盖 1km² 区域足够 --error-threshold 50 \ # root tile 误差 50 米 --compress-textures \ # 启用 ASTC 压缩(需系统有 astcenc) --verbose # 关键!看每步日志

参数说明:--compress-textures会调用astcenc将 PNG/JPG 转为.astc,体积降 70%,但需提前sudo apt install astcenc;--verbose输出中重点关注Generated tile with bounding volume: sphere [...]和Writing tileset.json,若前者缺失,说明 transform 解析失败,回退检查parse_osgb_origin.py输出。


4. 避坑:5 条让 90% 工程师重启三次的 OSGB→3DTiles 真实翻车现场

这些不是理论问题,是我们在模拟项目 X 的 37 次重导中,用git bisect定位到的具体代码行或配置项。每一条都附带 Cesium 控制台截图里的报错关键词,方便你对号入座。

4.1 现象:Cesium 控制台报Error: Failed to load tile: .../root.b3dm. TypeError: Cannot read property 'length' of undefined

原因:OSGB 中存在osg::PagedLOD节点,其子节点指向外部.osgb文件(如./pages/001.osgb),但3d-tiles-tools或osg23dtiles默认不递归加载这些外部引用,导致生成的root.b3dm里batchTable为空。
解决:用osgconv预处理,强制展开所有 PagedLOD:

osgconv --no-pagedlod input.osgb output_flattened.osgb

再用output_flattened.osgb作为转换输入。

4.2 现象:模型在 Cesium 里显示为纯白色,无任何纹理

原因:OSGB 纹理路径为相对路径../textures/roof.jpg,而转换工具未将../正确解析为上级目录,导致tileset.json中content.uri指向textures/roof.jpg,但实际文件在./output/textures/roof.jpg。
解决:手动修改tileset.json,在content.uri前加../,或用sed批量修复:

sed -i 's/"uri": "textures\//\"uri\": \"..\/textures\//g' ./3dtiles_output/tileset.json

4.3 现象:Cesium 加载后模型整体下沉 10 米,部分楼层“钻地”

原因:OSGB 的MatrixTransform中tz是相对于项目原点的高程(如 45.6 米),但--height参数被误设为海拔(如 50.2 米),导致 ECEF Z 值多加了 4.6 米。
解决:用parse_osgb_origin.py精确提取tz,--height必须等于该值,不要查地图标高。

4.4 现象:移动端首次加载卡死 20 秒,Network 面板显示大量pending请求

原因:geometricError设为 1 米,导致生成 12000+ 个瓦片,浏览器并发请求数超限(Chrome 默认 6 个)。
解决:按区域面积粗估 root error:error ≈ sqrt(area_in_m2) * 0.3。例如 1 km² 区域,error ≈ sqrt(1000000)*0.3 = 300米,再设--max-depth 4,瓦片数降至 200 以内。

4.5 现象:tileset.json中geometricError全为 0,Cesium 不触发 LOD 切换

原因:osg23dtiles未打补丁,或--error-threshold传入字符串"50"而非数字50(某些 shell 会自动转义)。
解决:检查tileset.json,若geometricError为 0,立即用jq重写:

jq '.root.geometricError = 50 | .root.children[].geometricError = 25' ./3dtiles_output/tileset.json > tmp.json && mv tmp.json ./3dtiles_output/tileset.json

5. 进阶验证:用 CesiumJS + 自定义 Inspector 实时诊断 tileset 健康度

导出完成不等于可用。我们写了一个轻量tileset-inspector.js,注入 Cesium Viewer,实时显示当前可见瓦片的geometricError、boundingVolume类型、纹理加载状态、GPU 内存占用,比反复改tileset.json效率高 10 倍。

5.1 在 Cesium Viewer 中注入诊断脚本

// tileset-inspector.js function injectTilesetInspector(viewer) { const container = viewer.container; const div = document.createElement('div'); div.id = 'tileset-inspector'; div.style.cssText = ` position: absolute; top: 10px; right: 10px; background: rgba(0,0,0,0.7); color: #fff; padding: 8px; font-size: 12px; border-radius: 4px; max-width: 300px; overflow-y: auto; max-height: 200px; `; container.appendChild(div); function updateInfo() { const tiles = viewer.scene.globe._surface.tileProvider._tilesToRender; const info = []; tiles.forEach(tile => { if (tile.content && tile.content.ready) { const err = tile.geometricError?.toFixed(1) || 'N/A'; const bvType = tile.boundingVolume?.region ? 'region' : tile.boundingVolume?.box ? 'box' : 'sphere'; const texLoaded = tile.content.textures?.length || 0; info.push(`ID:${tile.id} | Err:${err}m | BV:${bvType} | Tex:${texLoaded}`); } }); div.innerHTML = `<strong>Visible Tiles (${info.length}):</strong><br>` + info.join('<br>'); } viewer.scene.postRender.addEventListener(updateInfo); return () => viewer.scene.postRender.removeEventListener(updateInfo); } // 使用方式(在 new Cesium.Viewer 后调用) const inspector = injectTilesetInspector(viewer); // 想关闭时调用 inspector();

效果:打开开发者工具,拖拽视角,右上角实时刷新当前屏幕内加载的瓦片列表。若看到Err:0.0m,立刻知道geometricError有问题;若Tex:0占比高,说明纹理路径错误;若ID数量突增至 50+,说明geometricError设太小。

5.2 用3d-tiles-validator做离线合规性扫描

Cesium 官方提供的3d-tiles-validator(npm 包)能检测tileset.json结构、URI 可达性、boundingVolume有效性,但默认不校验 ECEF 坐标合法性。我们给它加了一个--ecef-check模式:

# 安装增强版 npm install -g @cesiumjs/3d-tiles-validator@latest # 扫描 tileset(含 ECEF 校验) 3d-tiles-validator ./3dtiles_output/tileset.json \ --ecef-check \ --verbose # 输出示例: # ✅ tileset.json syntax valid # ✅ root.b3dm exists and parses # ✅ boundingVolume.sphere: [x,y,z,r] is finite # ⚠️ tile 0/0.b3dm: r=1234.5m > 1000m, may cause LOD overloading # ❌ tile 1/2.b3dm: boundingVolume.sphere.z = -6370000.0m < Earth radius, invalid ECEF

关键逻辑:--ecef-check会读取tileset.json中所有transform矩阵,提取cx,cy,cz,检查是否满足x²+y²+z² > (6370000)²(地表以上),否则报invalid ECEF。这是防止模型“钻地”的最后一道防线。

5.3 我的落地习惯:三遍检查法

  1. 第一遍(导出后 5 分钟内):用parse_osgb_origin.py和compute_ecef_bounds.py手动核对tileset.json中root.transform和root.boundingVolume是否与脚本输出一致。不一致?立刻停,重跑。
  2. 第二遍(Cesium 加载后):打开tileset-inspector.js,拖拽旋转,确认Visible Tiles数量稳定在 10~30,Err值随距离平滑变化(如 50m → 100m → 200m),无0.0m。
  3. 第三遍(上线前 1 小时):用3d-tiles-validator --ecef-check全量扫描,修复所有❌,将⚠️条目记入运维文档,注明“已知限制,不影响功能”。

这三遍下来,99% 的线上事故能拦在发布前。工具只是杠杆,支点永远是人对空间数据的理解。希望帮到你。

本文还有配套的精品资源,点击获取

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/10/14 18:43:15

PMP计算题全攻略:挣值管理、三点估算与关键路径一次讲透

PMP考试里最让人头疼的&#xff0c;往往不是那些长篇大论的场景题&#xff0c;而是夹杂在题干里需要动笔算一算的计算题。我见过不少考生&#xff0c;前面敏捷题做得行云流水&#xff0c;一到计算题就卡壳&#xff0c;不是因为题有多难&#xff0c;而是因为公式太多、场景太杂&…

作者头像 李华
网站建设 2026/10/14 18:43:10

Viwepager 分页组件完全指南:从状态管理、接口适配到避坑调试

简介&#xff1a;这是一份基于Unity3D的图片轮播项目源码与工程配置包&#xff0c;适合想学习UI交互、动画过渡及资源管理的初中级开发者。压缩包内共405个文件&#xff0c;以PNG/JPG图片素材、C#脚本、Unity资源&#xff08;asset/unity/prefab&#xff09;和meta配置文件为主…

作者头像 李华
网站建设 2026/10/14 18:43:07

微信小程序组件按需注入未通过?HBuilderX配置与排坑实战

很多用HBuilderX开发微信小程序的朋友&#xff0c;应该都遇到过这个提示&#xff1a;在微信开发者工具的“本地设置”里勾选“启动组件按需注入”&#xff0c;或者在代码里配置了lazyCodeLoading&#xff0c;结果却提示或表现为“启用组件按需注入未通过”。我当时第一次遇到时…

作者头像 李华
网站建设 2026/10/14 18:41:54

微信小程序案例 3.5 计时器(增加背景文字:时间如流水)

一、案例简介 本案例是微信小程序计时器实现&#xff0c;基础功能实现计时、暂停、重置。扩展需求&#xff1a;在时间显示界面底层添加背景文字 “时间如流水”&#xff0c;作为页面装饰文字。本案例学习定时器、时间戳、页面布局 z-index 层级控制。 二、实现思路 计时原理&am…

作者头像 李华
网站建设 2026/10/14 18:41:17

jdk1.8.0_231.zip 部署指南:环境变量、多版本共存与容器化实践

简介&#xff1a;JDK1.8.0_231是Oracle于2019年发布的Java开发工具包64位版本&#xff0c;面向需要在Windows平台搭建Java开发环境的程序员与运维人员&#xff0c;尤其适合希望免安装、快速部署JDK的开发者。压缩包共1473个文件&#xff0c;约172.9MB&#xff0c;以705个jar核心…

作者头像 李华