【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 main

import (
	_ "embed" //导入` "embed"`包,即使`无显式的使用`
	"fmt"
)

//go:embed hello.txt
var data string

func main() {
	fmt.Println(data)
}
  • 运行demo
    在这里插入图片描述

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

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

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


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

在这里插入图片描述

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

在这里插入图片描述


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

  • 使用重命名:

在这里插入图片描述

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

控制台输入如下命令:

.\embed.exe

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

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


嵌入byte slice

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

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


demo
package main

import (
	_ "embed"
	"fmt"
)

//go:embed hello.txt
var data []byte

func main() {
	//将字节数组string化,输出字节数组的内容。
	fmt.Println(string(data))
}

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

执行结果如下:

在这里插入图片描述

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

在这里插入图片描述

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


嵌入文件系统FS

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

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

在这里插入图片描述

嵌入一个文件
package main

import (
	"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 main

import (
	"embed"
	_ "embed"
	"fmt"
)

//go:embed hello.txt hello1.txt
var data embed.FS

func 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 main

import (
	"embed"
	_ "embed"
	"fmt"
)

//go:embed dir
var data embed.FS

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

运行结果如下:

在这里插入图片描述


获取文件的内容
package main

import (
	"embed"
	_ "embed"
	"fmt"
	"path"
)

//go:embed dir
var data embed.FS

func main() {
	//读取文件夹dir
	dir, _ := 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稳定的有序遍历的方式

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

freemarker模板引擎结合node puppeteer库实现html生成图片

效果图: 先看效果图,以下是基于freemarker模板渲染数据,puppeteer加载html中的js及最后图片生成: 背景: 目前为止,后台java根据html模板或者一个网页路径生成图片,都不支持flex布局及最新的c…

Spring篇----第一篇

系列文章目录 文章目录 系列文章目录前言一、不同版本的 Spring Framework 有哪些主要功能?二、什么是 Spring Framework?三、列举 Spring Framework 的优点。四、Spring Framework 有哪些不同的功能?前言 前些天发现了一个巨牛的人工智能学习网站,通俗易懂,风趣幽默,忍…

二进制部署k8s集群之cni网络插件

目录 k8s的三种网络模式 pod内容器之间的通信 同一个node节点中pod之间通信 不同的node节点的pod之间通信 flannel网络插件 flannel的三种工作方式 VxLAN host-GW UDP Flannel udp 模式 Flannel VXLAN 模式 flannel插件的三大模式的总结 calico网络插件 k8s 组网…

高速DRAM的training

随着每一代接口(Interface)和存储(memory)的频率和速率的提高,信号采样以及传输变得越来越困难,因为数据眼(data eyes)越来越小。 为了帮助高速 I/O 握手,接口和存储支持越来越多的Training Modes,系统设计人员必须将这些Trainin…

Linux之JAVA环境配置jdkTomcatMySQL

目录 一. 安装jdk 1.1 查询是否有jdk 1.2 解压 1.3 配置环境变量 二. 安装Tomcat(开机自启动) 2.1 解压 2.2 启动tomcat 2.3 防火墙设置 2.4 创建启动脚本(设置自启动,服务器开启即启动) 三. MySQL安装(…

【蓝桥杯省赛真题27】python纸张数量 中小学青少年组蓝桥杯比赛python编程省赛真题解析

目录 python纸张数量 一、题目要求 1、编程实现 2、输入输出 二、算法分析 三、程序编写 四、程序说明 五、运行结果 六、考点分析 七、 推荐资料 1、蓝桥杯比赛 2、考级资料 3、其它资料 python纸张数量 第十二届蓝桥杯青少年组python比赛选拔赛真题 一、题目要…

前后端分离vue.js+nodejs学生考勤请假系统 _fbo36

此系统设计主要采用的是nodejs语言来进行开发,采用vue框架技术,框架分为三层,分别是控制层Controller,业务处理层Service,持久层dao,能够采用多层次管理开发,对于各个模块设计制作有一定的安全性…

备考2025年考研数学(三):2015-2024年考研数学真题练一练

今天,我们继续分享2015年-2024年的考研数学三选择题,随机做5道真题,并提供解析。看看正在备考2025年考研的你能做对几道。 考研数学和政治、英语一项,都是拉分大户,但是数学如果掌握了技巧,吃透了知识点的话…

科普GAI:走进生成式人工智能的世界

今天,我们来聊聊一个科技界热门话题——GAI(Generative Artificial Intelligence),也就是生成式人工智能。顾名思义,GAI是指那些能够自己“生”出新内容的人工智能系统,就像一位永不停歇的创新者&#xff0…

vue3+js 实现记住密码功能

常见的几种实现方式 1 基于spring security 的remember me 功能 ​​​​​​​ localStorage 除非主动清除localStorage 里的信息 ,不然永远存在,关闭浏览器之后下次启动仍然存在 存放数据大小一般为5M 不与服务器进行交互通信 cookies 可以…

Docker容器实战

"爱在,地图上,剥落~" Mysql 容器化安装 我们可以在 docker hub上,进入mysql的镜像仓库,找到适合的版本。 直接拉取镜像: docker pull mysql:latest 我们知道 msyql 的默认端口是 3306 ,而且有密码&#x…

黑马JavaWeb开发跟学(一)Web前端开发HTML、CSS基础

黑马JavaWeb开发一.Web前端开发HTML、CSS基础 引子、Web开发介绍传统路线本课程全新路线本课程适用人群课程收获一、什么是web开发二、网站的工作流程三、网站的开发模式四、网站的开发技术 前端开发基础一、前端开发二、HTML & CSS2.1 HTML快速入门2.1.1 操作第一步第二步…

蓝桥杯14届计算思维国赛U8组包含真题和答案

十四届蓝桥杯国赛考试计算思维 U8 组 答案在底部 第一题 以下选项中,( )是由美国计算机协会设立,对在计算机领域内作出重要贡献的个人授予的奖项 。A.图灵奖 C.菲尔兹奖 B.诺贝尔奖 D.普利策奖 第二题 希希去吃寿司。餐台上摆出了许多食物,可供大家自选。如下图所示。 …

2_怎么看原理图之协议类接口之UART笔记

通信双方先约定通信速率,如波特率115200 一开始时,2440这边维持高电平 1> 开始发送时,由2440将(RxD0)高电平拉低,并持续一个T的时间(为了让PC机可以反应过来),T1/波…

高级语言期末2011级A卷

1.编写函数&#xff0c;判定正整数m和n&#xff08;均至少为2&#xff09;是否满足&#xff1a;数m为数n可分解的最小质因数&#xff08;数n可分解的最小质因数为整除n的最小质数&#xff09; 提示&#xff1a;判定m为质数且m是n的最小因数 #include <stdio.h> #include…

信息安全计划:它是什么、为什么需要一个以及如何开始

每个组织都需要一个信息安全计划&#xff0c;因为数据已成为世界上最有价值的商品。与所有珍贵的东西一样&#xff0c;数据受到管理机构的严格监管&#xff0c;并且受到每个人&#xff08;包括骗子&#xff09;的觊觎。这就是网络犯罪不断增加的原因——与日益严格的合规环境同…

神经网络系列---激活函数

文章目录 激活函数Sigmoid 激活函数Tanh激活函数ReLU激活函数Leaky ReLU激活函数Parametric ReLU激活函数 &#xff08;自适应Leaky ReLU激活函数&#xff09;ELU激活函数SeLU激活函数Softmax 激活函数Swish 激活函数Maxout激活函数Softplus激活函数 激活函数 一般来说&#xf…

第三篇:CamX日志打印系统

第三篇:CamX日志打印系统 高通camx的日志主要分2大模块,UMD (user mode driver) 和KMD( kerner mode driver),也就是用户层和kernel层日志。 下面就来看下这2大块日志debug的时候该如何设置? 一、UDM日志 高通camx camera debug日志格式如下: CamX: [][] : . 例子:Ca…

ASM-HEMT模型中的射频参数提取

ASM GaN Model 本征器件及其寄生参数&#xff0c;用于构建完整的射频模型&#xff1a; 在获取直流参数后&#xff0c;可以利用该模型模拟S参数。为此&#xff0c;需要考虑寄生组件&#xff0c;并围绕模型构建一个子电路来表示所有寄生电容和电感。实际布局相关的寄生元件以及测…

AI:136-基于深度学习的图像生成与风格迁移

🚀点击这里跳转到本专栏,可查阅专栏顶置最新的指南宝典~ 🎉🎊🎉 你的技术旅程将在这里启航! 从基础到实践,深入学习。无论你是初学者还是经验丰富的老手,对于本专栏案例和项目实践都有参考学习意义。 ✨✨✨ 每一个案例都附带关键代码,详细讲解供大家学习,希望…