基于TopoJSON与OpenLayers的离线地图开发:Blob转JSON报错问询
嘿,我看你遇到的这个问题挺典型的——用TopoJSON+OpenLayers做离线地图,把JSON转成Blob存进IndexedDB(用Dexie封装)后,取出来转回JSON给OpenLayers用的时候,出现了[object%20Promise]的404错误。这大概率是你没处理好异步操作,直接把Promise对象当成资源路径传给OpenLayers了,咱们一步步来解决:
错误原因分析
[object%20Promise]是Promise对象被转成字符串后的URL编码,说明你在给OpenLayers提供数据源的时候,没有等待从IndexedDB取Blob、转JSON的异步操作完成,直接把Promise本身传了进去。OpenLayers把这个Promise当成了资源URL去请求,自然找不到对应的资源,返回404状态。
分步解决方案
1. 先避开异步操作的常见坑
Dexie的所有数据库操作都是异步的,返回的是Promise对象,你不能直接同步获取值。比如下面是错误的示范:
// ❌ 错误:直接把Promise当URL用了 const blobUrl = db.jsons.get(1).then(item => URL.createObjectURL(item.blob)); // OpenLayers加载时用了这个blobUrl,其实它是个Promise,不是字符串URL new ol.source.Vector({ format: new ol.format.TopoJSON(), url: blobUrl });
2. 等待异步完成后再初始化OpenLayers数据源
你需要用async/await或者.then()链式调用,等待Blob取出并生成URL后,再传给OpenLayers:
方法一:用async/await(更直观)
async function loadOfflineMap() { const db = new Dexie("json_db"); db.version(1).stores({ jsons: '++id' }); // 等待从DB取出Blob数据 const jsonItem = await db.jsons.get(1); // 假设你要取id为1的记录 if (!jsonItem) { console.error("数据库里没找到对应的TopoJSON数据哦"); return; } // 生成Blob的可访问URL const blobUrl = URL.createObjectURL(jsonItem.blob); // 初始化OpenLayers矢量数据源 const vectorSource = new ol.source.Vector({ format: new ol.format.TopoJSON(), url: blobUrl }); // 后续把数据源添加到图层、再添加到地图的操作... const vectorLayer = new ol.layer.Vector({ source: vectorSource }); // 比如 map.addLayer(vectorLayer); } // 调用加载函数 loadOfflineMap();
方法二:用.then()链式调用
const db = new Dexie("json_db"); db.version(1).stores({ jsons: '++id' }); db.jsons.get(1) .then(jsonItem => { if (!jsonItem) throw new Error("未找到目标TopoJSON数据"); // 生成Blob URL return URL.createObjectURL(jsonItem.blob); }) .then(blobUrl => { // 初始化数据源 const vectorSource = new ol.source.Vector({ format: new ol.format.TopoJSON(), url: blobUrl }); // 构建图层并添加到地图... }) .catch(err => console.error("加载离线地图失败:", err));
3. 额外优化:用完Blob URL记得释放内存
当你不再需要这个Blob URL的时候,要调用URL.revokeObjectURL(blobUrl)来释放浏览器内存,避免内存泄漏:
// 比如在数据源加载完成后释放 vectorSource.on('change', function(e) { if (vectorSource.getState() === 'ready') { URL.revokeObjectURL(blobUrl); } });
4. 检查Blob存入DB时的正确性
还要确认你存入DB的Blob是正确生成的,比如从远程URL获取TopoJSON转Blob的代码:
async function getBlobAndPutInDB(url) { const response = await fetch(url); const jsonText = await response.text(); // 注意MIME类型要设置为application/json,确保OpenLayers能正确解析 const blob = new Blob([jsonText], { type: 'application/json' }); await db.jsons.add({ blob: blob }); console.log("TopoJSON数据已成功存入IndexedDB"); }
总结
核心问题就是异步操作没处理到位,把Promise对象当成了实际的URL值。只要等待异步操作完成,拿到真实的Blob URL再传给OpenLayers,这个404错误就能解决啦。
内容的提问来源于stack exchange,提问作者Nilsono

