###### 1.灵感来源
主要是看到这个开源项目:
https://github.com/jteeuwen/go-bindata
该项目就是将文件生成go代码,编译出来的可执行程序可以创建携带的文件。我之前有个工具需要7za.exe,因为怕别人的电脑上没有这个文件,所以我想在我代码中判断环境不存在7za.exe或md5值不正确的时候,自动生成正确的7za.exe。当时就是使用上面的库,但是我仔细研究了一下源码发现生成的go源文件还是有点大,而且我也不需要太多复杂功能,因此我自己写了一个简化版的代码。
###### 2.放上源码
```go
package main
import (
"compress/zlib"
"fmt"
"io"
"log"
"os"
"path/filepath"
"strings"
)
func main() {
if len(os.Args) != 2 {
fmt.Printf("Usage: %s file\n", os.Args[0])
return
}
fName := os.Args[1]
fInfo, err := os.Stat(fName)
if (err != nil && os.IsNotExist(err)) || fInfo.IsDir() {
log.Fatal("file not find or is dir.") // 不存在 或 是文件夹,报错
}
fr, err := os.Open(fName)
if err != nil {
log.Fatal(err)
}
defer fr.Close()
fw, err := os.Create(fName + ".go")
if err != nil {
log.Fatal(err)
}
defer fw.Close()
tExt := "_new" + filepath.Ext(fName)
tName := strings.Replace(filepath.Base(fName), ".", "_", -1)
fmt.Fprintf(fw, `package main
import (
"bytes"
"compress/zlib"
"io"
"log"
"os"
)
func main() {
fw, err := os.Create(`+"`%s`"+`)
if err != nil {
log.Fatal(err)
}
defer fw.Close()
data, err := Read_%s()
if err != nil {
log.Fatal(err)
}
fw.Write(data)
}
func Read_%s() ([]byte, error) {
data := []byte("`, fName+tExt, tName, tName)
zw := zlib.NewWriter(&StringWriter{w: fw})
if _, err = io.Copy(zw, fr); err != nil {
log.Fatal(err)
}
if err = zw.Close(); err != nil {
log.Fatal(err)
}
fmt.Fprint(fw, `")
zr, err := zlib.NewReader(NewStringReader(data))
if err != nil {
return nil, err
}
var out bytes.Buffer
if _, err = io.Copy(&out, zr); err != nil {
return nil, err
}
if err = zr.Close(); err != nil {
return nil, err
}
return out.Bytes(), nil
}
type StringReader struct {
buf []byte
off int
}
func fromHexChar(c byte) byte {
switch {
case '0' <= c && c <= '9':
return c - '0'
case 'a' <= c && c <= 'f':
return c - 'a' + 10
}
return 0
}
func NewStringReader(p []byte) *StringReader {
n := len(p) / 2
for i := 0; i < n; i++ {
p[i] = fromHexChar(p[2*i])<<4 | fromHexChar(p[2*i+1])
}
return &StringReader{buf: p[:n], off: 0}
}
func (sr *StringReader) Read(p []byte) (int, error) {
if len(sr.buf) <= sr.off {
if len(p) == 0 {
return 0, nil
}
return 0, io.EOF
}
n := copy(p, sr.buf[sr.off:])
sr.off += n
return n, nil
}`)
}
type StringWriter struct {
w io.Writer
}
func (sw *StringWriter) Write(p []byte) (n int, err error) {
if len(p) == 0 {
return
}
var (
b byte
buf = make([]byte, 2)
lowerHex = "0123456789abcdef"
)
for n, b = range p {
buf[0] = lowerHex[b>>4]
buf[1] = lowerHex[b&0xf]
sw.w.Write(buf)
}
n++
return
}
```
###### 3.讲解用法
```
1.我的工具用法
# .\pack.exe 7za.exe
2.开源go-bindata用法
go-bindata.exe 7za.exe
3.按照大小排序文件
# ls -lth
total 6.5M
-rw-r--r-- 1 * 197121 1.5M 3月 22 09:25 bindata.go
-rw-r--r-- 1 * 197121 755K 3月 22 09:25 7za.exe.go
-rwxr-xr-x 1 * 197121 2.3M 3月 22 09:09 pack.exe*
-rwxr-xr-x 1 * 197121 661K 11月 9 18:16 7za.exe*
4.分析
bindata.go是开源工具产生的文件
7za.exe.go是我的工具产生的文件,明显我产生的文件要小很多,原因是开源工具存储的[]byte每个字节转"\x12",而我的直接就是"12"少了2字节数据,会是我生成的2倍。
5.还原文件
go run 7za.exe.go
diff 7za.exe 7za.exe_new.exe
执行生成的go文件,会得到一个还原的文件,将还原的文件和源文件对比没有差别,完美!
```
###### 4.总结
做这个主要场景就是方便使用者,无需关心附件的问题。有些软件会将附件和可执行程序打成压缩包,这也是一种解决方案,就看怎么权衡了。
特别注意:对于那种已经压缩过的文件可能生成的文件不会压缩太多,这个自行百度为什么。像".zip,.mp3,.exe"等已经压缩过的文件如果使用zlib压缩后效果不明显则还是建议不要再次压缩了,毕竟效果不好还浪费解压时间。
有疑问加站长微信联系(非本文作者))