【Golang】Golang使用embed加载、打包静态资源文件

【Golang】Golang使用embed加载、打包静态资源文件

大家好 我是寸铁👊
总结了一篇Golang使用embed加载静态资源文件的文章✨
喜欢的小伙伴可以点点关注 💝

前言

事情是这样的:前不久,有同学问我,golang怎么把静态资源文件打包成一个可执行文件呢?便于后序服务器资源的部署

在这里插入图片描述

经本人同意授权,图像码掉。

这不今天这个问题的答案就来啦!

背景

Go编译的程序非常适合部署,如果没有通过CGO引用其它的库的话,我们一般编译出来的可执行二进制文件都是单个的文件,非常适合复制和部署。在实际使用中,除了二进制文件,可能还需要一些配置文件,或者静态文件,比如html模板、静态的图片、CSS、javascript等文件,如何这些文件也能嵌入到二进制文件中,那就太美妙,我们只需复制、执行单个的可执行文件即可。

一些开源的项目很久以前就开始做这方面的工作,比如gobuffalo/packrmarkbates/pkgerrakyll/statikknadh/stuffbin等等,但是不管怎么说这些都是第三方提供的功能,如果Go官方能内建支持就好了。2019末一个提案被提出issue#35950,期望Go官方编译器支持嵌入静态文件。后来Russ Cox专门写了一个设计文档Go command support for embedded static assets, 并最终实现了它。


应用场景

为什么需要embed包?

  • 部署过程更简单。传统部署要么需要将静态资源与已编译程序打包在一起上传,或者使用dockerdockerfile自动化前者,这在精神上是很麻烦的。
  • 确保程序的完整性。在运行过程中损坏丢失静态资源通常会影响程序的正常运行。
  • 独立控制程序所需的静态资源。

最常见的方法(例如静态网站的后端程序)要求将程序连同其所依赖的html模板cssjs图片以及静态资源的路径一起上传到生产服务器。必须正确配置Web服务器,以便用户访问它

现在,我们将所有这些资源都嵌入到程序中。我们只需要部署一个二进制文件并为程序本身配置它们即可。部署过程已大大简化!

常用场景

以下列举一些静态资源文件需要被嵌入到程序中的常用场景:

  • Go模板:模板文件必须可用于二进制文件(模板文件需要对二进制文件可用)。 对于Web服务器二进制文件或那些通过提供init命令的CLI应用程序,这是一个相当常见的用例。 在没有嵌入的情况下,模板通常内联在代码中。例如示例qbec init的init命令
  • 静态web服务:有时,静态文件(如index.html或其他HTML,JavaScript和CSS文件之类的静态文件)需要使用golang服务器二进制文件进行传输,以便用户可以运行服务器并访问这些文件。例如示例web server中嵌入静态资源文件
  • 数据库迁移:另一个使用场景是通过嵌入文件被用于数据库迁移脚本。参考示例数据库迁移文件

定义

embed是在Go 1.16中新加包。它通过//go:embed指令,可以在编译阶段静态资源文件打包进编译好的程序(exe)中,并提供访问这些文件的能力

嵌入

  • 对于单个的文件,支持嵌入类型为stringbyte slice
  • 对于多个文件和文件夹,支持嵌入为新的文件系统FS
  • 比如导入 "embed"包,即使无显式的使用
  • go:embed指令用来嵌入,必须紧跟着嵌入后的变量名
  • 只支持嵌入为string, byte sliceembed.FS三种类型,这三种类型的别名(alias)命名类型(如type S string)都不可以!

同学们,暂时看不懂别着急,后面会逐个对上面的特性进行使用!

语法说明

  • go:embed指令用来嵌入,必须紧跟着嵌入后的变量名
//go:embed 要嵌入的文件

如下图片说明:

  • hello.txt文件嵌入到data这个字符串变量

在这里插入图片描述

  • 导入 "embed"包,即使无显式的使用
    在这里插入图片描述

基本使用

嵌入字符串

对于单个的文件,支持嵌入类型为stringbyte slice

  • 先编写hello.txt文件,文件内容可以自定义,用于后面的各种操作。

在这里插入图片描述


demo
  • 编写demo用于测试能否嵌入字符串,demo如下:
package mainimport (_ "embed" //导入` "embed"`包,即使`无显式的使用`"fmt"
)//go:embed hello.txt
var data stringfunc main() {fmt.Println(data)
}
  • 运行demo
    在这里插入图片描述

如上图,可以打印出嵌入文件的字符串变量的内容!

这样就实现把一个txt文件嵌入到字符串变量啦!

是不是很简单,后面还有更强大的功能,继续往下走!


  • 接着,我们使用go build一键生成可执行文件(exe)

在这里插入图片描述

  • 生成后,会在文件夹中出现如下exe文件:

在这里插入图片描述


这时,我们可以不妨大胆把原来要嵌入的文件hello.txt重命名或者直接删掉,看看能不能通过执行exe文件输出原来嵌入的文件的内容?

  • 使用重命名:

在这里插入图片描述

  • 使用删除:
    在这里插入图片描述

控制台输入如下命令:

.\embed.exe

运行结果如下:
在这里插入图片描述

可以发现删除或者重命名文件后,仍然可以通过去执行生成的二进制文件去输出原来嵌入文件的内容! 这就是嵌入功能的强大之处!


嵌入byte slice

对于单个的文件,支持嵌入类型为stringbyte slice

将上面data的数据类型改为[]byte即可


demo
package mainimport (_ "embed""fmt"
)//go:embed hello.txt
var data []bytefunc main() {//将字节数组string化,输出字节数组的内容。fmt.Println(string(data))
}

测试一下,能否输出文件的内容!

执行结果如下:

在这里插入图片描述

再测试一下运行生成的可执行文件,结果如下:

在这里插入图片描述

可以发现照常可以输出原来嵌入文件的内容!


嵌入文件系统FS

对于多个文件和文件夹,支持嵌入为新的文件系统FS

使用embed.FS嵌入的文件变量,具有3种功能,如下所示:

在这里插入图片描述

嵌入一个文件
package mainimport ("embed"_ "embed""fmt"
)//go:embed hello.txt
var data embed.FS //嵌入文件系统func main() {//data嵌入了一个文件系统,文件系统中有一个文件叫hello.txt//使用ReadFile读取这个文件的内容read, err := data.ReadFile("hello.txt")//输出内容fmt.Println(string(read), err)
}

运行结果如下:

在这里插入图片描述

再测试一下运行生成的可执行文件,结果如下:

在这里插入图片描述

可以发现照常可以输出原来嵌入文件的内容!


嵌入多个文件
  • 编写另一个文件的内容如下:

在这里插入图片描述

  • 嵌入多个文件很简单,只需要在后面添加文件即可。
//go:embed hello.txt hello1.txt
var data embed.FS
  • demo
package mainimport ("embed"_ "embed""fmt"
)//go:embed hello.txt hello1.txt
var data embed.FSfunc main() {read, err := data.ReadFile("hello.txt")fmt.Println(string(read), err)read1, err := data.ReadFile("hello1.txt")fmt.Println(string(read1), err)
}

执行结果如下:

在这里插入图片描述

再测试一下运行生成的可执行文件,结果如下:

在这里插入图片描述

嵌入一个文件夹
  • 创建一个文件夹dir 再把之前的文件给转移到上面去。
    在这里插入图片描述

  • demo

打印文件的名字

Info()的结构体信息如下:

// FileInfo 接口描述了一个文件,由 Stat 方法返回。
type FileInfo interface {Name() string       // 文件的基本名称Size() int64        // 对于普通文件,以字节为单位的长度;对于其他文件类型,取决于系统Mode() FileMode     // 文件的模式位ModTime() time.Time // 修改时间IsDir() bool        // 是否为目录的简写,等同于 Mode().IsDir()Sys() any           // 底层数据源(可能返回 nil)
}

打印一下文件的名字

package mainimport ("embed"_ "embed""fmt"
)//go:embed dir
var data embed.FSfunc main() {//读取文件夹dirdir, _ := data.ReadDir("dir")//entry为遍历dir文件夹中的每个文件for _, entry := range dir {info, _ := entry.Info()  // 获取文件的信息fmt.Println(info.Name()) // 获取文件中的名字}
}

运行结果如下:

在这里插入图片描述


获取文件的内容
package mainimport ("embed"_ "embed""fmt""path"
)//go:embed dir
var data embed.FSfunc main() {//读取文件夹dirdir, _ := data.ReadDir("dir")//entry为遍历dir文件夹中的每个文件for _, entry := range dir {info, _ := entry.Info() // 获取文件的信息//使用data的ReadFile方法打开文件//使用path.Join拼接文件的路径file, err := data.ReadFile(path.Join("dir", info.Name()))//输出内容fmt.Println(string(file), err)}
}

运行结果如下:

在这里插入图片描述

注意事项

  • go embed的使用只能用于包级别声明的变量(全局变量)

不能放在方法中!

在这里插入图片描述


结语

Go语言中的embed包是在Go 1.16版本中引入的,用于将文件内容嵌入到Go程序中。
尽管embed提供了一种方便的方式来嵌入文件,但它也有一些缺点:

  1. 编译时间较长: 当项目中嵌入的文件数量较多或文件较大时,embed可能导致编译时间变长。每次修改嵌入的文件,都需要重新构建整个程序。

  2. 不支持动态更新: 由于嵌入的文件在编译时就被固定在可执行文件中,因此无法在运行时动态更新嵌入的文件内容。如果需要在运行时更新文件,可能需要使用其他方法,如将文件存储在磁盘上并在需要时读取。

  3. 可执行文件大小增加: 将文件嵌入到可执行文件中会增加可执行文件的大小。对于一些嵌入了大量或大型文件的应用程序,这可能会导致可执行文件较大影响部署和传输时间

  4. 不适用于所有场景embed适用于将静态文件嵌入到程序中,但不适用于所有场景。例如,如果需要对配置文件进行频繁的写入和修改,可能会选择将文件保存在独立的目录中。

总体而言,embed是一个强大的工具,特别适用于需要将资源文件打包可执行文件中的情况。但在选择使用时,需要权衡它的优点和缺点,并根据项目的需求和约束进行选择!


看到这里的小伙伴,恭喜你又掌握了一个技能👊
希望大家能取得胜利,坚持就是胜利💪
我是寸铁!我们下期再见💕


往期好文💕

保姆级教程

【保姆级教程】Windows11下go-zero的etcd安装与初步使用

【保姆级教程】Windows11安装go-zero代码生成工具goctl、protoc、go-zero

【Go-Zero】手把手带你在goland中创建api文件并设置高亮


报错解决

【Go-Zero】Error: user.api 27:9 syntax error: expected ‘:‘ | ‘IDENT‘ | ‘INT‘, got ‘(‘ 报错解决方案及api路由注意事项

【Go-Zero】Error: only one service expected goctl一键转换生成rpc服务错误解决方案

【Go-Zero】【error】 failed to initialize database, got error Error 1045 (28000):报错解决方案

【Go-Zero】Error 1045 (28000): Access denied for user ‘root‘@‘localhost‘ (using password: YES)报错解决方案

【Go-Zero】type mismatch for field “Auth.AccessSecret“, expect “string“, actual “number“报错解决方案

【Go-Zero】Error: user.api 30:2 syntax error: expected ‘)‘ | ‘KEY‘, got ‘IDENT‘报错解决方案

【Go-Zero】Windows启动rpc服务报错panic:context deadline exceeded解决方案


Go面试向

【Go面试向】defer与time.sleep初探

【Go面试向】defer与return的执行顺序初探

【Go面试向】Go程序的执行顺序

【Go面试向】rune和byte类型的认识与使用

【Go面试向】实现map稳定的有序遍历的方式

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.hqwc.cn/news/491713.html

如若内容造成侵权/违法违规/事实不符,请联系编程知识网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

STM32单片机基本原理与应用(九)

SDIO/SD卡实验 实验内容 将SD卡插入实训平台并烧写程序,开机后TFTLCD屏幕上会显示是否成功初始化SD卡并显示SD卡容量。 电路原理图 实验原理 SD卡的通信方式有两种:SPI和SDIO。SD卡有五种寄存器,如下表 SD 卡的指令由 6 个字节组成&…

2024年2月20日v1.0.5更新·优雅草便民工具youyacao-tools

2024年2月20日v1.0.5更新优雅草便民工具youyacao-tools apk下载 https://fenfacun.youyacao.com/tools105.apk 介绍 优雅草便民工具是一款由成都市一颗优雅草科技有限公司打造的便民查询公益工具,2024年1月17日正式发布v1.0.0版本,本工具为了方便大众免…

pclpy OC-Tree “体素内的邻居搜索”、“K 最近邻搜索”和“半径内的邻居搜索”

pclpy OC-Tree “体素内的邻居搜索”、“K 最近邻搜索”和“半径内的邻居搜索” 一、算法原理1.介绍2.“体素内的邻居搜索”、“K 最近邻搜索”和“半径内的邻居搜索” 二、代码三、结果1.原点云2.体素内的邻居搜索3.K 最近邻搜索4.半径内的邻居搜索 四、相关数据 一、算法原理…

python实现线下缓存最优算法

对于现代计算机为了加快数据存储速度,一般会采用多级缓存的方法,以最简单的二级缓存来说,数据会存放在两个地方,一个地方就是存在内存当中,另一个存放的地方就是存放在硬盘当中,但是这两个地方数据读取的速…

面向面试的机器学习知识点(4)——分类模型

省流版: 本文介绍机器学习中的回归算法:逻辑回归、KNN、SVM、随机森林和XGBoost。作为机器学习的有监督学习方法,分类模型是最重要也是最常见的一类算法,在数据分析等岗位的笔试面试中都是常客,非常值得深入研究&…

【elasticsearch实战】知识库文件系统检索工具FSCrawler

需求背景 最近有一个需求需要建设一个知识库文档检索系统,这些知识库物料附件的文档居多,有较多文档格式如:PDF, Open Office, MS Office等,需要将这些格式的文件转化成文本格式,写入elasticsearch 的全文检索索引&am…

JAVA--反射机制

目录 1. 反射(Reflection)的概念 1.1 反射的出现背景 1.2 反射概述 1.3 Java反射机制研究及应用 1.4 反射相关的主要API 1.5 反射的优缺点 2. 理解Class类并获取Class实例 2.1 理解Class 2.1.1 理论上 2.1.2 内存结构上 2.2 获取Class类的实例(四种方法) 2.3 哪些类…

本地部署ChatGPT

发布一下我之前做的一个本地大模型部署,不需要API key,但要有自己的账号 利用Docker 的Pandora做本地ChatGPT模型部署 先下载安装Docker,设置好运行如下 会要求升级核心,cmd运行如下命令就OK 安装Pandora 再管理员cmd中输入如下命令拉取Pandora镜像 docker pull pengzhi…

Makefile静态库动态库的构建和链接之工程实用篇

静态库和动态库的构建和链接 现有C工程目录结构如下&#xff1a; add.h int add(int a, int b);add.cpp #include "add.h"int add(int a, int b) {return ab; }main.cpp #include <iostream> #include "add.h"int main() {std::cout << a…

openssl3.2 - crypto-mdebug被弃用后, 内存泄漏检查的替代方法

文章目录 openssl3.2 - crypto-mdebug被弃用后, 内存泄漏检查的替代方法概述笔记查看特性列表openssl3.2编译脚本 - 加入enable-crypto-mdebug看看有没有替代内存诊断的方法?main.cppmy_openSSL_lib.hmy_openSSL_lib.c备注备注END openssl3.2 - crypto-mdebug被弃用后, 内存泄…

TransformerEncoder影评测试

import os import keras import tensorflow as tf from keras import layers import numpy as np#加载数据 def load_data(data_dir):"""data_dir&#xff1a;train的目录或test的目录输出&#xff1a;X&#xff1a;评论的字符串列表y&#xff1a;标签列表&…

再探二分法

推荐阅读 智能化校园&#xff1a;深入探讨云端管理系统设计与实现&#xff08;一&#xff09; 智能化校园&#xff1a;深入探讨云端管理系统设计与实现&#xff08;二&#xff09; 文章目录 推荐阅读二分查找题目思路解法左闭右闭式写法左闭右开式写法 二分查找 题目 给定一个…