在项目开发中,难免会遇到静态资源文件。比如 css js 配置文件等等
业务背景:
遇到一个业务需要,解析手机号归属地,一个 phone.dat 资源文件,如果不使用 go-bindata 就需要将 phone.dat 部署的服务上,Dockerfile 使用 add 追加到 docker 里面
解决办法使用 go-bindata,将 phone.dat 生成 dat.go 文件
安装
安装完之后会在,gopath/bin 目录下生成 go-bindata 可执行文件
go get -d github.com/go-bindata/go-bindata/...
目录结构
common
tool
dat
phone.dat
dat.go
utils
phone.go // 解析手机号归属地
cd 到 common 目录下 执行
go-bindata -pkg=tool -o dat.go dat/
获取配置的时候需要和生成 dat.go 的目录保持一致
phone.go
package utils
import (
"bytes"
"errors"
"fmt"
"omp/omp-common/tool"
)
const (
CMCC byte = iota + 0x01 // 中国移动
CUCC // 中国联通
CTCC // 中国电信
CTCC_v // 电信虚拟运营商
CUCC_v // 联通虚拟运营商
CMCC_v // 移动虚拟运营商
INT_LEN = 4
CHAR_LEN = 1
HEAD_LENGTH = 8
PHONE_INDEX_LENGTH = 9
PHONE_DAT = "dat/phone.dat"
)
type PhoneRecord struct {
PhoneNum string
Province string
City string
ZipCode string
AreaZone string
CardType string
}
var (content []byte
CardTypemap = map[byte]string{
CMCC: " 中国移动 ",
CUCC: " 中国联通 ",
CTCC: " 中国电信 ",
CTCC_v: " 中国电信虚拟运营商 ",
CUCC_v: " 中国联通虚拟运营商 ",
CMCC_v: " 中国移动虚拟运营商 ",
}
total_len, firstoffset int32
)
func init() {
//var err error
//content, err = ioutil.ReadFile(PHONE_DAT) // 原写法
var err error
content, err = tool.Asset(PHONE_DAT) // 使用 go-bindata 写法
if err != nil {panic(err)
}
total_len = int32(len(content))
firstoffset = get4(content[INT_LEN : INT_LEN*2])
}
func Debug() {fmt.Println(version())
fmt.Println(totalRecord())
fmt.Println(firstRecordOffset())
}
func (pr PhoneRecord) String() string {return fmt.Sprintf("PhoneNum: %s\nAreaZone: %s\nCardType: %s\nCity: %s\nZipCode: %s\nProvince: %s\n", pr.PhoneNum, pr.AreaZone, pr.CardType, pr.City, pr.ZipCode, pr.Province)
}
func get4(b []byte) int32 {if len(b) < 4 {return 0}
return int32(b[0]) | int32(b[1])<<8 | int32(b[2])<<16 | int32(b[3])<<24
}
func getN(s string) (uint32, error) {
var n, cutoff, maxVal uint32
i := 0
base := 10
cutoff = (1<<32-1)/10 + 1
maxVal = 1<<uint(32) - 1
for ; i < len(s); i++ {
var v byte
d := s[i]
switch {
case '0' <= d && d <= '9':
v = d - '0'
case 'a' <= d && d <= 'z':
v = d - 'a' + 10
case 'A' <= d && d <= 'Z':
v = d - 'A' + 10
default:
return 0, errors.New("invalid syntax")
}
if v >= byte(base) {return 0, errors.New("invalid syntax")
}
if n >= cutoff {
// n*base overflows
n = (1<<32 - 1)
return n, errors.New("value out of range")
}
n *= uint32(base)
n1 := n + uint32(v)
if n1 < n || n1 > maxVal {
// n+v overflows
n = (1<<32 - 1)
return n, errors.New("value out of range")
}
n = n1
}
return n, nil
}
func version() string {return string(content[0:INT_LEN])
}
func totalRecord() int32 {return (int32(len(content)) - firstRecordOffset()) / PHONE_INDEX_LENGTH
}
func firstRecordOffset() int32 {return get4(content[INT_LEN : INT_LEN*2])
}
// 二分法查询 phone 数据
func Find(phone_num string) (pr *PhoneRecord, err error) {if len(phone_num) < 7 || len(phone_num) > 11 {return nil, errors.New("illegal phone length")
}
var left int32
phone_seven_int, err := getN(phone_num[0:7])
if err != nil {return nil, errors.New("illegal phone number")
}
phone_seven_int32 := int32(phone_seven_int)
right := (total_len - firstoffset) / PHONE_INDEX_LENGTH
for {
if left > right {break}
mid := (left + right) / 2
offset := firstoffset + mid*PHONE_INDEX_LENGTH
if offset >= total_len {break}
cur_phone := get4(content[offset : offset+INT_LEN])
record_offset := get4(content[offset+INT_LEN : offset+INT_LEN*2])
card_type := content[offset+INT_LEN*2 : offset+INT_LEN*2+CHAR_LEN][0]
switch {
case cur_phone > phone_seven_int32:
right = mid - 1
case cur_phone < phone_seven_int32:
left = mid + 1
default:
cbyte := content[record_offset:]
end_offset := int32(bytes.Index(cbyte, []byte("\000")))
data := bytes.Split(cbyte[:end_offset], []byte("|"))
card_str, ok := CardTypemap[card_type]
if !ok {card_str = " 未知电信运营商 "}
pr = &PhoneRecord{
PhoneNum: phone_num,
Province: string(data[0]),
City: string(data[1]),
ZipCode: string(data[2]),
AreaZone: string(data[3]),
CardType: card_str,
}
return
}
}
return nil, errors.New("phone's data not found")
}
其他项目使用的时候,就不需要在使用的项目中存在 dat/phone.dat 文件了
正文完