下载的 GeoJSON 对吗?SHA-256 校验和 + 版本化下载链接攻略
下载的 GeoJSON 对吗?给文件加密钥校验和(SHA-256)+ 版本化下载链接
线上提供行政区划 GeoJSON 下载的作者,应该都遇到过这种事:用户在群里贴一张报错图,说"你给的文件打不开/解析失败"。你本地打开好好的,问题多半出在传输环节——被中间 CDN 截断、浏览器缓存了一份旧货、或者用户手动"另存为"时下载不完整。手里有把校验和(checksum),三句话就能定位是谁的锅。这类问题在 GeoJSON下载 场景里尤其常见:边界文件动辄几百 KB,浏览器断点是家常便饭,而用户往往先怀疑是作者数据的问题。
这篇讲两件事:一是给下载的 GeoJSON 生成并展示 SHA-256 校验和,让用户下载后能自证文件没坏;二是用版本化下载链接,避免静态文件的缓存与覆盖写打架。全程用 Node 内置模块,不装额外依赖。
为什么字符串比对不可靠
很多人想偷懒:下载端再把文件读一遍,和线上字符串比对长短就完事。真不行。一个县界 GeoJSON 动辄几百 KB 到几 MB,字符串 equals 在文件末尾缺一两个字节时往往还能"通过",因为多数内容是重复的坐标串。要精确到字节级,得用密码学哈希。SHA-256 有雪崩效应:只要改一个坐标数字,哈希值就彻底不同,漏一个字节也逃不掉。
Node 里生成非常简单:
import { createHash } from 'node:crypto';
import { readFileSync } from 'node:fs';
function sha256File(path) {
const data = readFileSync(path);
return createHash('sha256').update(data).digest('hex');
}
const sum = sha256File('./510722-san台.geojson');
console.log('SHA-256:', sum);
createHash('sha256') 是原生实现,几百 MB 级的文件也就一两秒。相比 MD5,SHA-256 没有已知碰撞攻击,做文件完整性校验足够用。
把校验和写进 meta,随文件一起提供
光在本地算出来没意义,要让下载方也能拿到同一把"钥匙"。做法是在每次生成边界数据时,把校验和写进一个配套的清单文件,甚至可以在下载页面上直接展示:
| 文件 | 大小 | SHA-256 |
|---|---|---|
| 510722.geojson | 3.2 MB | 8f2c9a...e41d |
| 510722.geojson.gz | 1.1 MB | 77b6ee...29ac |
| 510722_osm.geojson | 4.8 MB | 0e3d77...c531 |
用户下载后,在终端跑:
# 下载方验证
shasum -a 256 510722.geojson
# 与页面上的值比对,一致即为完整文件
Windows 下没有 shasum?PowerShell 用 Get-FileHash:
Get-FileHash -Algorithm SHA256 .\510722.geojson
两边哈希对上,就能排除文件损坏;对不上,基本可以断定是缓存或断传。这一步把"你的文件坏了"这种含糊客诉,变成"下载方哈希不一致,请清缓存重试"的明确指引,售后沟通成本直线下降。
版本化下载链接:别再覆盖同一个文件名
比校验和更容易踩的,是静态文件的覆盖写问题。很多站点喜欢固定地址 download/510722.geojson,每次数据更新直接覆盖。这有个隐患:CDN 边缘缓存看到 URL 没变,可能给你返回旧版本;你更新了文件但用户拿到的还是上一个月的边界,两个版本混着流入流程图就很糟。
我的做法是文件名带版本号,更新即换 URL:
download/510722@2026-08.geojson
download/510722@2026-08.sha256
逻辑上,校验和本身也是一种版本锚点:把哈希的后 8 位拼进文件名 download/510722-geojson-8f2c9a41.geojson,只要内容不变 URL 就永远稳定缓存友好;内容一变 URL 跟着变,天然避开覆盖写。对边缘加速站尤其友好——能命中 CDN 的"不可变资产"缓存策略。
三个常见坑,逐个记下:
1. 校验和要基于字节而非文本。用 Node readFileSync 默认读出的是 Buffer,直接喂给 createHash 即可。别先 toString() 再哈希,BOM 或换行符差异会让哈希对不上。 2. gzip 文件单独算。510722.geojson 与 510722.geojson.gz 字节不同,哈希务必分开生成、分开展示,别拿未压缩的哈希去验证压缩包。 3. 下载链接带版本号后,记得同步更新 site 页与相关阅读里的链接。只改文件不改链接,用户点进旧文章还是旧版本——这恰恰是校验和最能暴露的问题。
实际落地时,我会把校验和生成直接塞进数据构建脚本的最后一步,而不是手工跑。比如在批量产出每个区县的 GeoJSON下载 文件后,顺手写一个 .sha256 清单:
import { createHash } from 'node:crypto';
import { readdirSync, readFileSync, writeFileSync } from 'node:fs';
const dir = './district';
let lines = [];
for (const f of readdirSync(dir).filter(f => f.endsWith('.geojson'))) {
const sum = createHash('sha256').update(readFileSync(`./district/${f}`)).digest('hex');
lines.push(`${sum} ${f}`);
}
writeFileSync('./district/.sha256', lines.join('\n'), 'utf8');
这样每次重建数据,.sha256 跟着更新,站点页直接读这份清单渲染展示,校验和就不会和实际文件脱节。构建与校验同源,是最省心的姿势。
小结
给 GeoJSON 下载加 SHA-256 校验和 + 版本化下载链接,是个半小时能落地、却实打实省售后的改动。哈希让"文件坏了"有了客观判据,版本号让静态缓存不再发错版本。首页的 GeoJSON下载 页配合上这套机制,传输与缓存这一环就算闭环了。下次用户再报文件打不开,先把校验和发给他对照,十有八九当场解决。
如果你用的下载方式比较特殊(比如走 Worker 按需包子集),校验和的接入点会稍有不同,可以留言聊聊你的场景。