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

441 次浏览次阅读
没有评论

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

正文完
 0
评论(没有评论)