go可执行程序携带附件

jan-bar · · 928 次点击 · · 开始浏览    
这是一个创建于 的文章,其中的信息可能已经有所发展或是发生改变。

###### 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压缩后效果不明显则还是建议不要再次压缩了,毕竟效果不好还浪费解压时间。

有疑问加站长微信联系(非本文作者))

入群交流(和以上内容无关):加入Go大咖交流群,或添加微信:liuxiaoyan-s 备注:入群;或加QQ群:692541889

928 次点击  ∙  1 赞  
加入收藏 微博
添加一条新回复 (您需要 登录 后才能回复 没有账号 ?)
  • 请尽量让自己的回复能够对别人有帮助
  • 支持 Markdown 格式, **粗体**、~~删除线~~、`单行代码`
  • 支持 @ 本站用户;支持表情(输入 : 提示),见 Emoji cheat sheet
  • 图片支持拖拽、截图粘贴等方式上传