121

私はGoで小さなWebアプリケーションに取り組んでいます。これは、開発者のマシンでアプリケーション/Webサービスのデバッグを支援するツールとして使用することを目的としています。プログラムへのインターフェースは、HTMLだけでなく、JavaScript(機能用)、画像、CSS(スタイル用)を含むWebページです。私はこのアプリケーションをオープンソーシングすることを計画しているので、ユーザーは単にMakefileを実行できるはずであり、すべてのリソースは必要な場所に移動します。ただし、ファイルや依存関係をできるだけ少なくして実行可能ファイルを簡単に配布できるようにもしたいと思います。HTML / CSS / JSを実行可能ファイルにバンドルして、ユーザーが1つのファイルをダウンロードして心配するだけで済むようにする良い方法はありますか?


現在、私のアプリでは、静的ファイルの提供は次のようになっています。

// called via http.ListenAndServe
func switchboard(w http.ResponseWriter, r *http.Request) {

    // snipped dynamic routing...

    // look for static resource
    uri := r.URL.RequestURI()
    if fp, err := os.Open("static" + uri); err == nil {
        defer fp.Close()
        staticHandler(w, r, fp)
        return
    }

    // snipped blackhole route
}

したがって、非常に簡単です。要求されたファイルが静的ディレクトリに存在する場合は、ハンドラーを呼び出します。ハンドラーは、ファイルを開いて、Content-Type提供する前に適切な設定を試みます。私の考えでは、これが実際のファイルシステムに基づく必要がある理由はありません。コンパイルされたリソースがある場合は、リクエストURIでインデックスを作成し、そのように提供することができます。

これを行うための良い方法がない場合、またはこれを試みて間違った木を吠えている場合は、私に知らせてください。私は、エンドユーザーが管理するファイルをできるだけ少なくすることを望んでいると考えました。

よりも適切なタグがある場合は、それらを追加するか、私に知らせてください。

4

5 に答える 5

85

Go 1.16以降、goツールは静的ファイルを実行可能バイナリに直接埋め込むことをサポートしています。

embedパッケージをインポートし、//go:embedディレクティブを使用して、埋め込みたいファイルとそれらを保存したい変数をマークする必要があります。

実行可能ファイルにファイルを埋め込む3つの方法hello.txt

import "embed"

//go:embed hello.txt
var s string
print(s)

//go:embed hello.txt
var b []byte
print(string(b))

//go:embed hello.txt
var f embed.FS
data, _ := f.ReadFile("hello.txt")
print(string(data))

変数の型を使用embed.FSすると、単純なファイルシステムインターフェイスを提供する変数に複数のファイルを含めることもできます。

// content holds our static web server content.
//go:embed image/* template/*
//go:embed html/index.html
var content embed.FS

は、次のように使用net/httpする値からファイルを提供するためのサポートを備えています。embed.FShttp.FS()

http.Handle("/static/", http.StripPrefix("/static/", http.FileServer(http.FS(content))))

テンプレートパッケージはtext/template.ParseFS()html/template.ParseFS()関数とtext/template.Template.ParseFS()メソッドhtml/template.Template.ParseFS()を使用してテンプレートを解析することもできます。

template.ParseFS(content, "*.tmpl")

次の回答は、古いオプションを示しています(Go 1.16より前)。


テキストファイルの埋め込み

テキストファイルについて話している場合、それらはソースコード自体に簡単に埋め込むことができます。後ろの引用符を使用して、string次のようにリテラルを宣言します。

const html = `
<html>
<body>Example embedded HTML content.</body>
</html>
`

// Sending it:
w.Write([]byte(html))  // w is an io.Writer

最適化のヒント:

ほとんどの場合、リソースをに書き込むだけでよいので、変換io.Writerの結果を保存することもできます。[]byte

var html = []byte(`
<html><body>Example...</body></html>
`)

// Sending it:
w.Write(html)  // w is an io.Writer

注意する必要があるのは、生の文字列リテラルに逆引用符( `)を含めることができないことだけです。生の文字列リテラルにはシーケンスを含めることができないため(解釈された文字列リテラルとは異なり)、埋め込みたいテキストに逆引用符が含まれている場合は、次の例のように、生の文字列リテラルを分割し、逆引用符を解釈された文字列リテラルとして連結する必要があります。

var html = `<p>This is a back quote followed by a dot: ` + "`" + `.</p>`

これらの連結はコンパイラーによって実行されるため、パフォーマンスは影響を受けません。

バイナリファイルの埋め込み

バイトスライスとして保存

バイナリファイル(画像など)の場合、最もコンパクトで(結果のネイティブバイナリに関して)、最も効率的なのは、ファイルのコンテンツを[]byteソースコードに含めることです。これは、 go-bindataなどのサードパーティのtoos/librariesによって生成できます。

これにサードパーティのライブラリを使用したくない場合は、バイナリファイルを読み取り、ファイル[]byteの正確なコンテンツで初期化される型の変数を宣言するGoソースコードを出力する単純なコードスニペットを次に示します。

imgdata, err := ioutil.ReadFile("someimage.png")
if err != nil {
    panic(err)
}

fmt.Print("var imgdata = []byte{")
for i, v := range imgdata {
    if i > 0 {
        fmt.Print(", ")
    }
    fmt.Print(v)
}
fmt.Println("}")

ファイルに0〜16のバイトが含まれる場合の出力例(Go Playgroundで試してください):

var imgdata = []byte{0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15}

base64として保存string

ファイルが「大きすぎない」場合(ほとんどの画像/アイコンが適格)、他の実行可能なオプションもあります。ファイルの内容をBase64に変換し、stringそれをソースコードに保存できます。アプリケーションの起動時( )または必要に応じて、元のコンテンツfunc init()にデコードできます。Goは、パッケージ[]byte内のBase64エンコーディングを適切にサポートしています。encoding/base64

(バイナリ)ファイルをbase64に変換するのstringは、次のように簡単です。

data, err := ioutil.ReadFile("someimage.png")
if err != nil {
    panic(err)
}
fmt.Println(base64.StdEncoding.EncodeToString(data))

結果のbase64文字列をソースコードに保存します(例:const

デコードは1つの関数呼び出しです。

const imgBase64 = "<insert base64 string here>"

data, err := base64.StdEncoding.DecodeString(imgBase64) // data is of type []byte

引用どおりに保存string

base64として保存するよりも効率的ですが、ソースコードでは、バイナリデータの引用符で囲まれた文字列リテラルを保存する方が長い場合があります。strconv.Quote()次の関数を使用して、任意の文字列の引用符付き形式を取得できます。

data, err := ioutil.ReadFile("someimage.png")
if err != nil {
    panic(err)
}
fmt.Println(strconv.Quote(string(data))

0から64までの値を含むバイナリデータの場合、出力は次のようになります(Go Playgroundで試してください)。

"\x00\x01\x02\x03\x04\x05\x06\a\b\t\n\v\f\r\x0e\x0f\x10\x11\x12\x13\x14\x15\x16\x17\x18\x19\x1a\x1b\x1c\x1d\x1e\x1f !\"#$%&'()*+,-./0123456789:;<=>?"

strconv.Quote()(引用符を追加および追加することに注意してください。)

この引用符で囲まれた文字列をソースコードで直接使用できます。次に例を示します。

const imgdata = "\x00\x01\x02\x03\x04\x05\x06\a\b\t\n\v\f\r\x0e\x0f\x10\x11\x12\x13\x14\x15\x16\x17\x18\x19\x1a\x1b\x1c\x1d\x1e\x1f !\"#$%&'()*+,-./0123456789:;<=>?"

すぐに使用でき、デコードする必要はありません。引用符の解除は、コンパイル時にGoコンパイラによって行われます。

次のように必要な場合は、バイトスライスとして保存することもできます。

var imgdata = []byte("\x00\x01\x02\x03\x04\x05\x06\a\b\t\n\v\f\r\x0e\x0f\x10\x11\x12\x13\x14\x15\x16\x17\x18\x19\x1a\x1b\x1c\x1d\x1e\x1f !\"#$%&'()*+,-./0123456789:;<=>?")
于 2015-01-21T15:51:02.997 に答える
80

go-bindataパッケージは、あなたが興味を持っているもののようです。

https://github.com/go-bindata/go-bindata

これにより、静的ファイルをコードに埋め込むことができる関数呼び出しに変換でき、呼び出されたときにファイルコンテンツのバイトスライスが返されます。

于 2012-12-16T19:01:26.887 に答える
7

バンドルReactアプリケーション

たとえば、reactからのビルド出力は次のようになります。

build/favicon.ico
build/index.html
build/asset-manifest.json
build/static/css/**
build/static/js/**
build/manifest.json

このように使用すると、私たちが望んでいない(予期しない)go:embedコンテンツを提供します。http://localhost:port/build/index.html/build

//go:embed build/*
var static embed.FS

// ...
http.Handle("/", http.FileServer(http.FS(static)))

実際、次を使用して期待どおりに機能させるには、もう1つの手順を実行する必要がありますfs.Sub

package main

import (
    "embed"
    "io/fs"
    "log"
    "net/http"
)

//go:embed build/*
var static embed.FS

func main() {
    contentStatic, _ := fs.Sub(static, "build")
    http.Handle("/", http.FileServer(http.FS(contentStatic)))
    log.Fatal(http.ListenAndServe("localhost:8080", nil))
}

これで、http://localhost:8080期待どおりにWebアプリケーションを提供できるはずです。

AmitMittalのクレジット。

注:go:embed1.16以降が必要です。

于 2021-04-22T07:21:12.980 に答える
2

また、いくつかのエキゾチックな方法があります。Mavenプラグインを使用してGoLangプロジェクトをビルドし、JCPプリプロセッサを使用してバイナリブロックとテキストファイルをソースに埋め込むことができます。ケースコードは以下の行のようになります(そしていくつかの例はここにあります

var imageArray = []uint8{/*$binfile("./image.png","uint8[]")$*/}
于 2017-02-14T06:23:39.670 に答える
1

go-bindata別の回答で言及されている一般的な代替手段として、 mjibson / escも任意のファイルを埋め込みますが、ディレクトリツリーを特に便利に処理します。

于 2018-08-24T08:21:04.287 に答える