概述
最近在用node做一个shp文件的上传入库的功能,使用的是shapefile
库,但是在入库的时候由于dbf文件字符集的原因导致数据的入库失败或者不能正确的显示。于是几经搜索资料,找到了一个可行的方法,跟大家分享一下。
shapefile简介
Shapefile-js提供了编写简单的JavaScript程序以读取ESRI Shapefile 以及关联的属性文件的功能,它可以在网页端使用,也可在Node.js环境下使用,node安装后也可在终端使用。
shell
npm i shapefile
Node中使用:
js
var shapefile = require("shapefile");
shapefile.open("example.shp")
.then(source => source.read()
.then(function log(result) {
if (result.done) return;
console.log(result.value);
return source.read().then(log);
}))
.catch(error => console.error(error.stack));
web中使用:
html
<!DOCTYPE html>
<script src="https://unpkg.com/shapefile@0.6"></script>
<script>
shapefile.open("https://cdn.rawgit.com/mbostock/shapefile/master/test/points.shp")
.then(source => source.read()
.then(function log(result) {
if (result.done) return;
console.log(result.value);
return source.read().then(log);
}))
.catch(error => console.error(error.stack));
</script>
终端中可以快速将shp转换为json文件。
shell
shp2json example.shp
dbf文件
dbf文件由头记录
及数据记录
组成。头记录
定义该表的结构并包含与表相关的其他信息。
BDF文件头的详细格式如下表:

如上表,dbf文件的语言驱动ID在第29个字节。下表为各字符集对应的编码。
ID | Codepage | Description |
---|---|---|
1 | 0x01 | 437 |
2 | 0x02 | 850 |
3 | 0x03 | 1252 |
4 | 0x04 | 10000 |
8 | 0x08 | 865 |
9 | 0x09 | 437 |
10 | 0x0A | 850 |
11 | 0x0B | 437 |
13 | 0x0D | 437 |
14 | 0x0E | 850 |
15 | 0x0F | 437 |
16 | 0x10 | 850 |
17 | 0x11 | 437 |
18 | 0x12 | 850 |
19 | 0x13 | 932 |
20 | 0x14 | 850 |
21 | 0x15 | 437 |
22 | 0x16 | 850 |
23 | 0x17 | 865 |
24 | 0x18 | 437 |
25 | 0x19 | 437 |
26 | 0x1A | 850 |
27 | 0x1B | 437 |
28 | 0x1C | 863 |
29 | 0x1D | 850 |
31 | 0x1F | 852 |
34 | 0x22 | 852 |
35 | 0x23 | 852 |
36 | 0x24 | 860 |
37 | 0x25 | 850 |
38 | 0x26 | 866 |
55 | 0x37 | 850 |
64 | 0x40 | 852 |
77 | 0x4D | 936 |
78 | 0x4E | 949 |
79 | 0x4F | 950 |
80 | 0x50 | 874 |
87 | 0x57 | Current ANSI CP |
88 | 0x58 | 1252 |
89 | 0x59 | 1252 |
100 | 0x64 | 852 |
101 | 0x65 | 866 |
102 | 0x66 | 865 |
103 | 0x67 | 861 |
104 | 0x68 | 895 |
105 | 0x69 | 620 |
106 | 0x6A | 737 |
107 | 0x6B | 857 |
108 | 0x6C | 863 |
120 | 0x78 | 950 |
121 | 0x79 | 949 |
122 | 0x7A | 936 |
123 | 0x7B | 932 |
124 | 0x7C | 874 |
134 | 0x86 | 737 |
135 | 0x87 | 852 |
136 | 0x88 | 857 |
150 | 0x96 | 10007 |
151 | 0x97 | 10029 |
152 | 0x98 | 10006 |
200 | 0xC8 | 1250 |
201 | 0xC9 | 1251 |
202 | 0xCA | 1254 |
203 | 0xCB | 1253 |
204 | 0xCC | 1257 |
通过上表,我们可得知中文对应的Id为77,因此代码实现的时候我们可以这么做判断:
js
async parseFields(file) {
try {
// 检查DBF文件是否存在
const dbfExists = await existsAsync(file.dbfFilePath);
if (!dbfExists) {
logger.error(`DBF文件不存在: ${file.dbfFilePath}`);
return [];
}
console.log('start open Dbf file...', file.dbfFilePath);
// 读取DBF文件的前几个字节来检测编码
const buffer = await readFileAsync(file.dbfFilePath);
// DBF文件的语言驱动ID在第29个字节(0-based索引为28)
// http://shapelib.maptools.org/codepage.html?d=1563413688103
const languageDriverId = buffer[29];
let charset = 'UTF-8';
if (languageDriverId === 77) {
charset = 'GBK';
}
logger.info(`DBF文件字符集检测结果: ${charset} (languageDriverId: ${languageDriverId})`);
// 使用检测到的字符集打开DBF文件
let source = null;
try {
source = await shapefile.openDbf(file.dbfFilePath, {
encoding: charset
});
} catch (error) {
// 如果使用检测到的字符集打开失败,尝试使用GBK(中文环境常用)
logger.error(`使用${charset}打开DBF文件失败:`, error);
charset = 'GBK';
source = await shapefile.openDbf(file.dbfFilePath, {
encoding: charset
});
}
// 使用shapefile库读取DBF文件
const fields = [];
// 获取字段信息
if (source._fields) {
for (const field of source._fields) {
const fieldTypeDixt = {
C: 'String',
N: 'Number',
D: 'Date',
F: 'Number',
}
fields.push({
name: field.name,
type: fieldTypeDixt[field.type] || 'String',
size: field.length,
decimal: field.decimal
});
}
}
return {
fields: fields,
charset: charset
};
} catch (error) {
logger.error('解析字段信息失败:', error);
return [];
}
}
}