Quelle est la meilleure façon de regrouper des ressources statiques dans un programme Go? [fermé]

100

Je travaille sur une petite application Web dans Go qui est destinée à être utilisée comme un outil sur la machine d'un développeur pour aider à déboguer leurs applications / services Web. L'interface du programme est une page Web qui comprend non seulement le HTML, mais aussi du JavaScript (pour la fonctionnalité), des images et du CSS (pour le style). Je prévois d'ouvrir cette application, donc les utilisateurs devraient simplement pouvoir exécuter un Makefile et toutes les ressources iront là où elles doivent aller. Cependant, j'aimerais aussi pouvoir distribuer simplement un exécutable avec le moins de fichiers / dépendances possible. Existe-t-il un bon moyen de regrouper le HTML / CSS / JS avec l'exécutable, afin que les utilisateurs n'aient qu'à télécharger et à se soucier d'un seul fichier?


À l'heure actuelle, dans mon application, servir un fichier statique ressemble un peu à ceci:

// 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
}

C'est donc assez simple: si le fichier demandé existe dans mon répertoire statique, invoquez le gestionnaire, qui ouvre simplement le fichier et essaie de définir un bon Content-Typeavant de servir. Ma pensée était qu'il n'y avait aucune raison que cela doive être basé sur le système de fichiers réel: s'il y avait des ressources compilées, je pourrais simplement les indexer par l'URI de la requête et les servir comme telles.

S'il n'y a pas une bonne façon de faire cela, ou si j'aboie le mauvais arbre en essayant de le faire, faites-le moi savoir. J'ai juste pensé que l'utilisateur final apprécierait le moins de fichiers possible à gérer.

S'il y a des balises plus appropriées que , n'hésitez pas à les ajouter ou à me le faire savoir.

Jimmy Sawczuk
la source
Je viens de penser exactement à la même question aujourd'hui. La solution que je pourrais explorer est d'utiliser go generateavec un petit utilitaire de ligne de commande (fourni avec mon code source) pour convertir les fichiers en []bytetranches qui sont incorporées en tant que variables dans le code, de la même manière stringer(voir blog.golang.org / générer ).
Ralph

Réponses:

76

Le package go-bindata semble être ce qui vous intéresse.

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

Il vous permettra de convertir n'importe quel fichier statique en un appel de fonction qui peut être incorporé dans votre code et renverra une tranche d'octet du contenu du fichier lorsqu'il est appelé.

Daniel
la source
8
Le vote positif semble curieusement intéressé dans mon cas, mais je le ferai quand même: p Pour mémoire, ce n'est pas un package, mais un outil de ligne de commande.
jimt
Pour mémoire, c'est le chemin que j'ai emprunté avec mon projet. À un moment donné, @jimt a introduit de nouvelles fonctionnalités pour rendre les choses plus conviviales mais ne fournissait plus la granularité dont j'avais besoin, j'ai donc écrit mon propre outil qui a moins de fonctionnalités mais est conçu pour mon cas d'utilisation (j'utilise cet outil comme une sorte de préambule au processus de construction): github.com/jimmysawczuk/go-binary
Jimmy Sawczuk
37

Incorporation de fichiers texte

Si nous parlons de fichiers texte, ils peuvent facilement être intégrés dans le code source lui-même. Utilisez simplement les guillemets pour déclarer le stringlittéral comme ceci:

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

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

Conseil d'optimisation:

Comme la plupart du temps, vous n'aurez qu'à écrire la ressource dans un io.Writer, vous pouvez également stocker le résultat d'une []byteconversion:

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

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

La seule chose à laquelle vous devez faire attention est que les littéraux de chaîne bruts ne peuvent pas contenir le caractère guillemet arrière (`). Les littéraux de chaîne bruts ne peuvent pas contenir de séquences (contrairement aux littéraux de chaîne interprétés), donc si le texte que vous souhaitez incorporer contient des guillemets, vous devez casser le littéral de chaîne brute et concaténer les guillemets arrière comme des littéraux de chaîne interprétés, comme dans cet exemple:

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

Les performances ne sont pas affectées, car ces concaténations seront exécutées par le compilateur.

Incorporation de fichiers binaires

Stockage sous forme de tranche d'octet

Pour les fichiers binaires (par exemple des images), le plus compact (en ce qui concerne le binaire natif résultant) et le plus efficace serait d'avoir le contenu du fichier comme un []bytedans votre code source. Cela peut être généré par des toos / bibliothèques tiers comme go-bindata .

Si vous ne souhaitez pas utiliser une bibliothèque tierce pour cela, voici un simple extrait de code qui lit un fichier binaire et génère un code source Go qui déclare une variable de type []bytequi sera initialisée avec le contenu exact du fichier:

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("}")

Exemple de sortie si le fichier contiendrait des octets de 0 à 16 (essayez-le sur le Go Playground ):

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

Stockage en base64 string

Si le fichier n'est pas «trop volumineux» (la plupart des images / icônes sont admissibles), il existe également d'autres options viables. Vous pouvez convertir le contenu du fichier en Base64 stringet le stocker dans votre code source. Au démarrage de l'application ( func init()) ou si nécessaire, vous pouvez le décoder dans le []bytecontenu d' origine . Go a un bon support pour l'encodage Base64 dans le encoding/base64package.

La conversion d'un fichier (binaire) en base64 stringest aussi simple que:

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

Stockez la chaîne résultat base64 dans votre code source, par exemple sous forme de fichier const.

Le décoder est juste un appel de fonction:

const imgBase64 = "<insert base64 string here>"

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

Stockage comme indiqué string

Plus efficace que le stockage en base64, mais peut être plus long dans le code source, il stocke le littéral de chaîne entre guillemets des données binaires. Nous pouvons obtenir la forme citée de n'importe quelle chaîne en utilisant la strconv.Quote()fonction:

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

Pour les données binaires contenant des valeurs de 0 à 64, voici à quoi ressemblerait la sortie (essayez-la sur le 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:;<=>?"

(Notez que cela strconv.Quote()ajoute et ajoute un guillemet.)

Vous pouvez utiliser directement cette chaîne entre guillemets dans votre code source, par exemple:

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:;<=>?"

Il est prêt à l'emploi, pas besoin de le décoder; la suppression des guillemets est effectuée par le compilateur Go, au moment de la compilation.

Vous pouvez également le stocker sous forme de tranche d'octets si vous en avez besoin comme ça:

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:;<=>?")
icza
la source
existe-t-il un moyen de lier un shfichier à un exécutable go?
Kasun Siyambalapitiya
Je suppose que les données devraient être imgdata dans le premier extrait de code sous la section «stockage sous forme de tranche d'octet».
logique x 2
1
@deusexmachina Vous avez raison, corrigé. Le code sur le terrain de jeu était déjà correct.
icza
2

il y a aussi une manière exotique - j'utilise le plugin maven pour construire des projets GoLang et cela permet d'utiliser le préprocesseur JCP pour incorporer des blocs binaires et des fichiers texte dans les sources. Dans le cas, le code ressemble à la ligne ci-dessous ( et un exemple peut être trouvé ici )

var imageArray = []uint8{/*$binfile("./image.png","uint8[]")$*/}
Igor Maznitsa
la source
@est-il possible de lier un répertoire ayant un shou un exécutable comme ci
Kasun Siyambalapitiya
@KasunSiyambalapitiya Lier un répertoire? Lier un shfichier? Pas sûr de ce que vous voulez dire. Si vous voulez que tout dans un répertoire soit intégré, c'est quelque chose que j'ai fait go-bindata. Par exemple, si je place //go:generate $GOPATH/bin/go-bindata -prefix=data/ -pkg=$GOPACKAGE data/un fichier go (non généré), go generate ./...j'exécute go-bindata dans le répertoire du package, en intégrant tout dans un sous-répertoire de données mais avec le préfixe «data /» supprimé.
Mark
1

En tant qu'alternative populaire à celle go-bindatamentionnée dans une autre réponse, mjibson / esc intègre également des fichiers arbitraires, mais gère les arborescences de répertoires de manière particulièrement pratique.

robx
la source