最近几天,可能由于安全原因,ArcGIS 的底图(basemap)服务突然不可访问了,既导致了 ArcGIS Pro 客户端软件无法正常加载世界地图,也导致基于 ArcGIS Maps SDK for JavaScript 的 WebGIS 项目无法加载出底图,对工作和学习造成了一定影响。那么本文将分别介绍如何解决这两类问题。
01
—
ArcGIS Pro 客户端软件
打开 ArcGIS Pro 后,新建一个空白的地图工程,就会发现默认地图(World Topographic Map)无法加载出来,地图区域为一片空白,左侧名称出现红色感叹号。
实际上,就是因为 ArcGIS 的在线地图服务在国内暂时被屏蔽了。比如这个默认的世界地形图,它对应的服务链接地址可能为:
https://services.arcgisonline.com/ArcGIS/rest/services/World_Topo_Map/MapServer而这个链接已经无法直接打开了。
解决方式也容易,那就是用魔法/TZ。使用了工具之后,再打开 ArcGIS Pro 就可以正常看到地图了。
刚才的链接地址在魔法下就可以正常打开,如下图:
至于如何使用魔法/TZ,请参阅其他相关资料。当然有条件的话也可以自己搭建一个代理服务器实现,有需要可以参考我之前的文章:自主制作一个代理工具来访问ChatGPT
或者在我的网站技术博客中查阅:https://mengchen.cc/articles
02
—
ArcGIS JS SDK 的 Web 项目
由于底图服务访问被限制,因此所有使用到这类底图的应用(Web/iOS/Android等平台)都会受到影响,只有当客户端开启了魔法/TZ后才可正常访问。对于 ArcGIS Pro 来说,使用它的人基本都是 GIS 相关领域的学生或从业者,是具备一定科学上网的能力的,可以按第一章节所述的内容处理。但是基于 ArcGIS SDK 开发出的各类应用 App,它们的终端用户并非都是专业人士,这些用户在面对 App 中的空白地图时就会感到困惑,并不一定知道这是网络受限的问题,从而影响了用户体验。那么对于 ArcGIS 应用的开发者,应该如何解决这个问题,让终端用户能够正常访问呢?
本文以 WebGIS App 为例来说明如何解决底图加载的问题,其他平台的解决方案相似,可参考 ArcGIS 的官方文档。
ArcGIS 的 Web 应用通常会使用到 ArcGIS Maps SDK for JavaScript。发起请求底图服务的是浏览器端的 JavaScript,请求的 URL 就是 services.arcgisonline.com 等这类官方地址,在没有额外配置的环境下则会请求失败。
好在 ArcGIS JS SDK 提供了配置代理(proxy)的功能,文档地址:
https://developers.arcgis.com/javascript/latest/proxies/
可以配置哪些 ArcGIS 官方域名前缀的请求被转发到某个代理地址。我这里整理了若干域名,并且将它们都转发到我设置的一个代理服务器地址:
import * as urlUtils from "@arcgis/core/core/urlUtils.js";["https://basemaps.arcgis.com/","https://services.arcgisonline.com/","https://tiles.arcgis.com/","https://maps.arcgis.com/","https://static.arcgis.com/","https://vtiles.arcgis.com/","https://elevation3d.arcgis.com/","https://cdn.arcgis.com/",].forEach((prefix) => {urlUtils.addProxyRule({urlPrefix: prefix,proxyUrl: "https://your.proxy.domain"});});
其中 https://your.proxy.domain 为代理服务器地址,稍后我会说这个服务器应该怎么搭建。先说一下这段代码的含义,它将以上述 8 个 ArcGIS 相关的服务域名(如果有遗漏则补充即可)为前缀的请求,全部转发给 proxyUrl 指定的地址。官网文档好像没有说转发过去的 URL 最终是什么样,我试了一下,完整的 URL 如下:
https://your.proxy.domain/?https://services.arcgisonline.com/ArcGIS/rest/services/World_Topo_Map/MapServer?f=json也就是说,它把原来要请求的正常地址,直接作为一串 Query String 拼在了代理服务器 URL 的"?"后面,而且也没有做 URL encode。
那么接下来的重点就是这个 ArcGIS 代理服务器(your.proxy.domain)应该如何部署。总体来说,分为三步:
1、获取参数中的被代理URL(即原始请求 *.arcgis.com 的链接)
2、建立并得到海外节点(或魔法工具/TZ)的代理 IP 和端口
3、基于海外代理 IP 和端口,发起对被代理 URL 的请求
为了更加形象的表述,我们用流程图描述一下。
首先,在 ArcGIS 能正常访问的时候,业务流程如下图所示:
当使用了代理服务器后,业务流程变为下图:
其中 ArcGIS 代理服务器就是一个简单的 http Server,可以跟现有 WebGIS 项目部署在同一台机器,也可以单独部署。
这里我们以 Node.js 编程语言为例,使用 express 作为 http server 来搭建用于 ArcGIS Web 项目的代理服务器,也可以用其他任何编程语言(Python/PHP/Go/Java 等)来实现相同的功能。
1、获取参数中的被代理URL
由于这个 Query String 不是标准的 key=value&key=value 的形式,因此可以直接通过 req.url.substring(2) 获取 /? 之后的全部内容。主要代码如下:
const app = express()app.get("/", async(req, res) => {// target即为目标URL:https://services.arcgisonline.com/xxxxxxconst target = req.url.substring(2)// 通过fetch发起对target的请求 ...})
2、建立并得到海外节点(或魔法工具/TZ)的代理IP和端口
如果你自己使用了魔法/TZ,你也许可以知道你当前的代理IP和端口。例如,我很早之前用过的 Lantern,是能看到它的代理IP和端口,如下图。其他工具类似。
我目前使用的方式是通过 SSH Tunnel 与海外服务器的 TinyProxy 建立端口映射,以实现科学上网。我的代理 IP 和端口为 127.0.0.1:8888。
3、基于海外代理 IP 和端口,发起对被代理 URL 的请求
通过 node-fetch 发起 http(s) 请求,指定 agent 参数为代理,即可让 ArcGIS 代理服务器通过魔法/TZ (海外代理服务器)访问 ArcGIS 官方地图服务。主要代码如下:
const fetch = require("node-fetch")const HTTP_PROXY = "http://127.0.0.1:8888"const agent = new HttpsProxyAgent(HTTP_PROXY)const upstream = await fetch(target, {method: 'GET',agent: agent,// other options})// 处理upstream响应并返回
以上仅介绍了核心步骤,如果需要完整的代码,可以与我联系。也可以借助目前主流的 AI 工具(ChatGPT / DeepSeek / 豆包等)生成完整代码。
03
—
其他办法
