go go-bindata打包静态资源文件嵌入到二进制文件

作者: 太阳上的雨天 分类: Go 发布时间: 2022-04-01 12:46

在项目开发中,难免会遇到静态资源文件。比如 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文件了

发表回复

您的电子邮箱地址不会被公开。 必填项已用 * 标注