{"id":152187,"date":"2021-06-09T14:14:10","date_gmt":"2021-06-09T13:14:10","guid":{"rendered":"https:\/\/blog.jetbrains.com\/?post_type=go&#038;p=152187"},"modified":"2022-06-05T11:38:47","modified_gmt":"2022-06-05T10:38:47","slug":"how-to-use-go-embed-in-go-1-16","status":"publish","type":"go","link":"https:\/\/blog.jetbrains.com\/zh-hans\/go\/2021\/06\/09\/how-to-use-go-embed-in-go-1-16","title":{"rendered":"How to Use go:embed in Go"},"content":{"rendered":"\n<p>One of the most anticipated features of Go 1.16 is the support for embedding files and folders into the application binary at compile-time without using an external tool. This feature is also known as <em>go:embed<\/em>, and it gets its name from the compiler directive that makes this functionality possible: <em>\/\/go:embed<\/em>.<\/p>\n\n\n\n<p>With it, you can embed all web assets required to make a frontend application work. The build pipeline will simplify since the embedding step does not require any additional tooling to get all static files needed in the binary. At the same time, the deployment pipeline is predictable since you don\u2019t need to worry about deploying the static files and the problems that come with that, such as: making sure the relative paths are what the binary expects, the working directory is the correct one, the application has the proper permissions to read the files, etc. You just deploy the application binary and start it, and everything else works.<\/p>\n\n\n\n<p>Let&#8217;s see how we can use this feature to our advantage with an example webserver:<\/p>\n\n\n\n<ul><li>First, create a new Go modules project in GoLand, and make sure you use Go 1.16 or newer. The <em>go<\/em> directive in the <em>go.mod<\/em> file must be set to Go 1.16 or higher too.<\/li><\/ul>\n\n\n\n<pre class=\"EnlighterJSRAW\">\nmodule goembed.demo\n\ngo 1.16<\/pre>\n\n\n\n<ul><li>Our <em>main.go<\/em> file should look like this:<\/li><\/ul>\n\n\n\n<pre class=\"EnlighterJSRAW\">\npackage main\n\nimport (\n\t&quot;embed&quot;\n\t&quot;html\/template&quot;\n\t&quot;log&quot;\n\t&quot;net\/http&quot;\n)\n\nvar (\n\t\/\/go:embed resources\n\tres embed.FS\n\n\tpages = map&#091;string]string{\n\t\t&quot;\/&quot;: &quot;resources\/index.gohtml&quot;,\n\t}\n)\n\nfunc main() {\n\thttp.HandleFunc(&quot;\/&quot;, func(w http.ResponseWriter, r *http.Request) {\n\t\tpage, ok := pages&#091;r.URL.Path]\n\t\tif !ok {\n\t\t\tw.WriteHeader(http.StatusNotFound)\n\t\t\treturn\n\t\t}\n\t\ttpl, err := template.ParseFS(res, page)\n\t\tif err != nil {\n\t\t\tlog.Printf(&quot;page %s not found in pages cache...&quot;, r.RequestURI)\n\t\t\tw.WriteHeader(http.StatusInternalServerError)\n\t\t\treturn\n\t\t}\n\n\t\tw.Header().Set(&quot;Content-Type&quot;, &quot;text\/html&quot;)\n\t\tw.WriteHeader(http.StatusOK)\n\t\tdata := map&#091;string]interface{}{\n\t\t\t&quot;userAgent&quot;: r.UserAgent(),\n\t\t}\n\t\tif err := tpl.Execute(w, data); err != nil {\n\t\t\treturn\n\t\t}\n\t})\n\n\thttp.FileServer(http.FS(res))\n\n\tlog.Println(&quot;server started...&quot;)\n\terr := http.ListenAndServe(&quot;:8088&quot;, nil)\n\tif err != nil {\n\t\tpanic(err)\n\t}\n}<\/pre>\n\n\n\n<ul><li>Next, create a new <em>resources\/index.gohtml<\/em> file like the one below:<\/li><\/ul>\n\n\n\n<pre class=\"EnlighterJSRAW\">&lt;html lang=&quot;en&quot;&gt;\n&lt;head&gt;\n    &lt;meta charset=&quot;UTF-8&quot;\/&gt;\n    &lt;title&gt;go:embed demo&lt;\/title&gt;\n&lt;\/head&gt;\n&lt;body&gt;\n&lt;div&gt;\n    &lt;h1&gt;Hello, {{ .userAgent }}!&lt;\/h1&gt;\n    &lt;p&gt;If you see this, then go:embed worked!&lt;\/p&gt;\n&lt;\/div&gt;\n&lt;\/body&gt;\n&lt;\/html&gt;<\/pre>\n\n\n\n<ul><li>Finally, create a file called <em>check.http<\/em> at the root of the project. This will reduce the time it takes to test our code by making repeatable requests from GoLand rather than using the browser.<\/li><\/ul>\n\n\n\n<pre class=\"EnlighterJSRAW\">\nGET http:\/\/localhost:8088\/<\/pre>\n\n\n\n<p><strong><em>Note:<\/em><\/strong> If you need to, you can download a newer version of Go using GoLand either while creating the project or via <em>Settings\/Preferences | Go | GOROOT | + | Download &#8230;<\/em><\/p>\n\n\n\n<p>This is how the project layout should look:<\/p>\n\n\n\n<figure class=\"wp-block-image size-large\"><img decoding=\"async\" loading=\"lazy\" width=\"1500\" height=\"612\" src=\"https:\/\/blog.jetbrains.com\/wp-content\/uploads\/2021\/06\/project-layout-1.png\" alt=\"\" class=\"wp-image-152378\"\/><\/figure>\n\n\n\n<p>If we run this project, then test the request from the <em>check.http<\/em> file against our server, we get what we\u2019d expect: an HTML response that contains our Hello message and the &#8220;Apache-HttpClient&#8221; user agent.<\/p>\n\n\n\n<figure class=\"wp-block-image size-large\"><img decoding=\"async\" src=\"https:\/\/resources.jetbrains.com\/storage\/products\/blog\/wp-content\/uploads\/GoLand\/Go%20Embed%20support\/run%20http%20server%20and%20check.png\" data-gif-src=\"https:\/\/resources.jetbrains.com\/storage\/products\/blog\/wp-content\/uploads\/GoLand\/Go%20Embed%20support\/run%20http%20server%20and%20check.gif\"\/><\/figure>\n\n\n\n<p>At first, this might not look different than any other server responding to a request with our template code.<\/p>\n\n\n\n<p>However, if we change the code in the template without restarting the server, we&#8217;ll quickly notice that our output will not change unless we rebuild the binary. We can even remove the template, move the binary, or change its running directory, and the result will be similar. How does this work, then?<\/p>\n\n\n\n<h2 class=\"wp-block-heading\" id=\"how-go-embed-works\">A quick look at how go:embed works<\/h2>\n\n\n\n<p>We can isolate a few parts of our code that are involved in using this feature.<\/p>\n\n\n\n<p>We\u2019ll start with the imports section, where we can see that we are using a new package called <em>embed<\/em>. This package, combined with the comment <em>\/\/go:embed<\/em>, a <a href=\"https:\/\/golang.org\/cmd\/compile\/#hdr-Compiler_Directives\" target=\"_blank\" rel=\"noopener\">compiler directive<\/a>, tells the compiler that we intend to embed files or folders in the resulting binary.<\/p>\n\n\n\n<p>You need to follow this directive with a variable declaration to serve as the container for the embedded contents. The type of the variable can be a string, a slice of bytes, or <a href=\"https:\/\/pkg.go.dev\/embed#FS\" target=\"_blank\" rel=\"noopener\"><em>embed.FS<\/em><\/a> type.&nbsp;If you embed resources using the <em>embed.FS<\/em> type, they also get the benefit of being read-only and goroutine-safe.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\" id=\"goland-support-for-goembed\">GoLand support for go:embed<\/h2>\n\n\n\n<p>GoLand completion features come in handy while using the embed directive, helping you write the paths\/pattern.<\/p>\n\n\n\n<figure class=\"wp-block-image size-large\"><img decoding=\"async\" src=\"https:\/\/resources.jetbrains.com\/storage\/products\/blog\/wp-content\/uploads\/GoLand\/Go Embed support\/completion in go embed directives.png\" data-gif-src=\"https:\/\/resources.jetbrains.com\/storage\/products\/blog\/wp-content\/uploads\/GoLand\/Go Embed support\/completion in go embed directives.gif\"\/><\/figure>\n\n\n\n<p>You can also navigate to the embedded resource from the editor.<\/p>\n\n\n\n<figure class=\"wp-block-image size-large\"><img decoding=\"async\" src=\"https:\/\/resources.jetbrains.com\/storage\/products\/blog\/wp-content\/uploads\/GoLand\/Go Embed support\/navigate to definition.png\" data-gif-src=\"\"\/><\/figure>\n\n\n\n<p>What if you want to change the name of the resource you\u2019ve embedded? Or perhaps you want to change the whole directory structure? GoLand has you covered here too:<\/p>\n\n\n\n<figure class=\"wp-block-image size-large\"><img decoding=\"async\" src=\"https:\/\/resources.jetbrains.com\/storage\/products\/blog\/wp-content\/uploads\/GoLand\/Go%20Embed%20support\/refactoring%20support%20for%20go%20embed%20directive.png\" data-gif-src=\"https:\/\/resources.jetbrains.com\/storage\/products\/blog\/wp-content\/uploads\/GoLand\/Go%20Embed%20support\/refactoring%20support%20for%20go%20embed%20directive.gif\"\/><\/figure>\n\n\n\n<p><strong>Pro tip:<\/strong> You can embed resources into the binary from any file, not just the main one. This means that you can ship modules with resources that are transparently compiled into the end application.&nbsp;<br><strong>Pro tip:<\/strong> You can use the embedding feature in test files too! Try it out and let us know what you think.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\" id=\"limitations\">Limitations<\/h2>\n\n\n\n<p>Embedding empty folders is not supported. Embedding symlinks is not currently supported either.<\/p>\n\n\n\n<p>If you don&#8217;t plan to use <em>embed.FS<\/em>, then you can use <em>\/\/go:embed<\/em> to embed a single file. To do so, you must still import the <em>embed<\/em> package, but only <a href=\"https:\/\/golang.org\/doc\/effective_go#blank_import\" target=\"_blank\" rel=\"noopener\">for side effects<\/a>.<\/p>\n\n\n\n<figure class=\"wp-block-image size-large\"><img decoding=\"async\" src=\"https:\/\/resources.jetbrains.com\/storage\/products\/blog\/wp-content\/uploads\/GoLand\/Go%20Embed%20support\/fix%20missing%20embed%20directive.png\" data-gif-src=\"https:\/\/resources.jetbrains.com\/storage\/products\/blog\/wp-content\/uploads\/GoLand\/Go%20Embed%20support\/fix%20missing%20embed%20directive.gif\"\/><\/figure>\n\n\n\n<p>The embedding directive must not contain a space between the comment and &#8220;<em>go:<\/em>&#8220;.<\/p>\n\n\n\n<figure class=\"wp-block-image size-large\"><img decoding=\"async\" loading=\"lazy\" width=\"1650\" height=\"429\" src=\"https:\/\/blog.jetbrains.com\/wp-content\/uploads\/2021\/06\/go-embed-with-spaces-2-1.png\" alt=\"\" class=\"wp-image-152349\"\/><\/figure>\n\n\n\n<p>The embedded paths must exist and match the pattern. Otherwise, the compiler will abort with an error.<\/p>\n\n\n\n<figure class=\"wp-block-image size-large\"><img decoding=\"async\" loading=\"lazy\" width=\"1500\" height=\"419\" src=\"https:\/\/blog.jetbrains.com\/wp-content\/uploads\/2021\/06\/file-pattern-not-found-1.png\" alt=\"\" class=\"wp-image-152337\"\/><\/figure>\n\n\n\n<h2 class=\"wp-block-heading\" id=\"conclusion\">Conclusion<\/h2>\n\n\n\n<p>That&#8217;s it for now! We learned why and how to use Go 1.16\u2019s new embedding feature, took a look at how it works, and considered some caveats to remember when using it. We&#8217;ve also seen how GoLand helps you work with this feature and provides features such as completion, error detection, and more.<\/p>\n\n\n\n<p>We are looking forward to hearing from you about how you use this feature. You can leave us a note in the comments section below, <a href=\"https:\/\/twitter.com\/GoLandIDE\" target=\"_blank\" rel=\"noopener\">on Twitter<\/a>, <a href=\"https:\/\/app.slack.com\/client\/T029RQSE6\/C3FJ8M2PN\/\" target=\"_blank\" rel=\"noopener\">on the Gophers Slack<\/a>, or <a href=\"https:\/\/youtrack.jetbrains.com\/issues\/Go\" target=\"_blank\" rel=\"noopener\">our issue tracker<\/a> if you&#8217;d like to let us know about additional features you\u2019d like to see related to this or other Go functionality.<\/p>\n","protected":false},"author":828,"featured_media":0,"comment_status":"closed","ping_status":"closed","template":"","categories":[2347],"tags":[6472,6657,91],"cross-post-tag":[],"acf":[],"_links":{"self":[{"href":"https:\/\/blog.jetbrains.com\/zh-hans\/wp-json\/wp\/v2\/go\/152187"}],"collection":[{"href":"https:\/\/blog.jetbrains.com\/zh-hans\/wp-json\/wp\/v2\/go"}],"about":[{"href":"https:\/\/blog.jetbrains.com\/zh-hans\/wp-json\/wp\/v2\/types\/go"}],"author":[{"embeddable":true,"href":"https:\/\/blog.jetbrains.com\/zh-hans\/wp-json\/wp\/v2\/users\/828"}],"replies":[{"embeddable":true,"href":"https:\/\/blog.jetbrains.com\/zh-hans\/wp-json\/wp\/v2\/comments?post=152187"}],"version-history":[{"count":10,"href":"https:\/\/blog.jetbrains.com\/zh-hans\/wp-json\/wp\/v2\/go\/152187\/revisions"}],"predecessor-version":[{"id":253395,"href":"https:\/\/blog.jetbrains.com\/zh-hans\/wp-json\/wp\/v2\/go\/152187\/revisions\/253395"}],"wp:attachment":[{"href":"https:\/\/blog.jetbrains.com\/zh-hans\/wp-json\/wp\/v2\/media?parent=152187"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/blog.jetbrains.com\/zh-hans\/wp-json\/wp\/v2\/categories?post=152187"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/blog.jetbrains.com\/zh-hans\/wp-json\/wp\/v2\/tags?post=152187"},{"taxonomy":"cross-post-tag","embeddable":true,"href":"https:\/\/blog.jetbrains.com\/zh-hans\/wp-json\/wp\/v2\/cross-post-tag?post=152187"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}