将文件嵌入go代码,你的可执行程序可以携带其他文件了

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

```go package main import ( "compress/zlib" "fmt" "io" "log" "os" "path/filepath" ) func main() { type fileInfo struct { file string size int64 } fileList := make(map[string]fileInfo, len(os.Args)) for _, v := range os.Args[1:] { if f, err := os.Stat(v); err == nil || os.IsExist(err) { fileList[filepath.Base(v)] = fileInfo{file: v, size: f.Size()} } } if len(fileList) == 0 { log.Fatal(os.Args, "input error") } fw, err := os.Create("resources.go") if err != nil { log.Fatal(err) } defer fw.Close() fw.WriteString(`package main import ( "compress/zlib" "errors" "io" "os" ) func WriteBytesToFile(path, name string) error { ok, err := checkFileIsOk(path, name) if err != nil || ok { return err } zr, err := zlib.NewReader(NewStringReader(getBytesFromMap(name))) if err != nil { return err } fw, err := os.Create(path) if err != nil { return err } defer fw.Close() if _, err = io.Copy(fw, zr); err != nil { return err } return zr.Close() } func checkFileIsOk(path, name string) (bool, error) { size, ok := map[string]int64{ `) for k, v := range fileList { fmt.Fprintf(fw, " \"%s\": %d,\n", k, v.size) } fw.WriteString(` }[name] if !ok { return false, errors.New("name not find") } f, err := os.Stat(path) return (err == nil || os.IsExist(err)) && size == f.Size(), nil } func getBytesFromMap(name string) ([]byte, int) { data := map[string]struct { s string l int }{ `) for k, v := range fileList { fmt.Fprintf(fw, " \"%s\": {s: \"", k) l, err := writeDataToFile(fw, v.file) if err != nil { log.Fatal(err) } fmt.Fprintf(fw, "\",\n l: %d},\n", l) } fw.WriteString(` }[name] return []byte(data.s), data.l } type StringReader struct { buf []byte off, l int } func NewStringReader(p []byte, l int) io.Reader { for i := 0; i < l; i++ { p[i] = (p[2*i]-'a')<<4 | (p[2*i+1] - 'a') } return &StringReader{buf: p[:l:l], off: 0, l: l} } func (sr *StringReader) Read(p []byte) (int, error) { if sr.off >= sr.l { if len(p) == 0 { return 0, nil } return 0, io.EOF } n := copy(p, sr.buf[sr.off:]) sr.off += n return n, nil }`) } func writeDataToFile(fw *os.File, file string) (int, error) { fr, err := os.Open(file) if err != nil { return 0, err } defer fr.Close() sw := &StringWriter{w: fw, wLen: 0} zw := zlib.NewWriter(sw) if _, err = io.Copy(zw, fr); err != nil { return 0, err } if err = zw.Close(); err != nil { return 0, err } return sw.wLen, nil } type StringWriter struct { wLen int w io.Writer } const lowerHex = "abcdefghijklmnop" func (sw *StringWriter) Write(p []byte) (n int, err error) { if len(p) > 0 { var ( b byte buf = make([]byte, 2) ) for n, b = range p { buf[0], buf[1] = lowerHex[b>>4], lowerHex[b&0xf] if _, err = sw.w.Write(buf); err != nil { return } } n++ sw.wLen += n } return } ``` 使用方法很简单: add_file_to_go.exe a.txt b.jpg c.exe 此时会产生一个resources.go文件,加入到你的项目中。 在程序运行开始的时候调用: WriteBytesToFile("c:\\a.txt", "a.txt") WriteBytesToFile("c:\\b.jpg", "b.jpg") WriteBytesToFile("c:\\c.exe", "c.exe") 既可以将文件写入到指定目录,如果文件已经存在则不会重复写入。最早想的是使用md5值判断文件是否改变,但想想有点耗性能, 而且大部分时间这些文件是不会改变的,因此改成判断文件大小,及如果有人改写了这些文件,下次运行时会重新将正确的文件写入。 我使用了zlib的压缩方法将文件压缩了一次,保证生成的resources.go很小。灵感来源于这个开源项目 [点击跳转](https://github.com/jteeuwen/go-bindata) 但是实现完全是我自己写的,思想和那个开源项目类似。

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

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

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