Qual è il modo migliore per raggruppare le risorse statiche in un programma Go? [chiuso]


100

Sto lavorando a una piccola applicazione Web in Go che deve essere utilizzata come strumento sulla macchina di uno sviluppatore per aiutare a eseguire il debug delle loro applicazioni / servizi Web. L'interfaccia del programma è una pagina web che include non solo l'HTML, ma anche alcuni JavaScript (per funzionalità), immagini e CSS (per lo styling). Sto progettando di rendere open-source questa applicazione, quindi gli utenti dovrebbero essere semplicemente in grado di eseguire un Makefile e tutte le risorse andranno dove devono andare. Tuttavia, mi piacerebbe anche essere in grado di distribuire semplicemente un eseguibile con il minor numero possibile di file / dipendenze. C'è un buon modo per raggruppare HTML / CSS / JS con l'eseguibile, in modo che gli utenti debbano solo scaricare e preoccuparsi di un file?


In questo momento, nella mia app, servire un file statico ha un aspetto un po 'simile a questo:

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

Quindi è piuttosto semplice: se il file richiesto esiste nella mia directory statica, invoca il gestore, che semplicemente apre il file e cerca di impostare un bene Content-Typeprima di servire. Il mio pensiero era che non c'è motivo per cui questo debba essere basato sul vero filesystem: se ci fossero risorse compilate, potrei semplicemente indicizzarle tramite l'URI della richiesta e servirle come tali.

Se non c'è un buon modo per farlo, o sto abbaiando sull'albero sbagliato provando a farlo, fammelo sapere. Ho solo pensato che l'utente finale apprezzerebbe il minor numero di file possibile da gestire.

Se sono presenti tag più appropriati di , sentiti libero di aggiungerli o fammi sapere.



Oggi ho pensato esattamente alla stessa domanda. La soluzione che potrei esplorare è quella di utilizzare go generatecon una piccola utility da riga di comando (confezionata con il mio codice sorgente) per convertire i file in []bytesezioni che sono incorporate come variabili nel codice, in modo simile a come stringerfa (vedi blog.golang.org / generare ).
Ralph

Risposte:


76

Il pacchetto go-bindata sembra che potrebbe essere quello che ti interessa.

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

Ti consentirà di convertire qualsiasi file statico in una chiamata di funzione che può essere incorporata nel codice e restituirà una fetta di byte del contenuto del file quando viene chiamato.


8
Upvoting questo sembra stranamente egoistico nel mio caso, ma lo farò comunque: p Per la cronaca, però, non è un pacchetto, ma uno strumento da riga di comando.
jimt

Solo per la cronaca, questo è il percorso che ho intrapreso con il mio progetto. Ad un certo punto @jimt ha introdotto alcune nuove funzionalità per rendere le cose più user-friendly ma non ha più fornito la granularità di cui avevo bisogno, quindi ho scritto il mio strumento che ha meno funzionalità ma è progettato per il mio caso d'uso (lo uso come una sorta di preambolo al processo di compilazione): github.com/jimmysawczuk/go-binary
Jimmy Sawczuk

37

Incorporamento di file di testo

Se parliamo di file di testo, possono essere facilmente incorporati nel codice sorgente stesso. Usa semplicemente le virgolette posteriori per dichiarare il stringletterale in questo modo:

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

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

Suggerimento per l'ottimizzazione:

Poiché la maggior parte delle volte dovrai solo scrivere la risorsa su un io.Writer, puoi anche memorizzare il risultato di una []byteconversione:

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

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

L'unica cosa a cui devi stare attento è che i letterali stringa non elaborati non possono contenere il carattere di virgolette (`). I letterali stringa non elaborati non possono contenere sequenze (a differenza dei letterali stringa interpretati), quindi se il testo che si desidera incorporare contiene virgolette all'indietro, è necessario rompere il valore letterale stringa non elaborato e concatenare le virgolette all'indietro come valori letterali stringa interpretati, come in questo esempio:

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

Le prestazioni non vengono influenzate, poiché queste concatenazioni verranno eseguite dal compilatore.

Incorporamento di file binari

Archiviazione come fetta di byte

Per i file binari (es. Immagini) più compatto (per quanto riguarda il binario nativo risultante) e più efficiente sarebbe avere il contenuto del file come un []bytenel codice sorgente. Questo può essere generato da strumenti / librerie di terze parti come go-bindata .

Se non desideri utilizzare una libreria di terze parti per questo, ecco un semplice frammento di codice che legge un file binario e genera il codice sorgente Go che dichiara una variabile di tipo []byteche verrà inizializzata con il contenuto esatto del file:

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

Output di esempio se il file conterrebbe byte da 0 a 16 (provalo su Go Playground ):

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

Memorizzazione come base64 string

Se il file non è "troppo grande" (la maggior parte delle immagini / icone è qualificata), ci sono anche altre opzioni possibili. Puoi convertire il contenuto del file in un Base64 stringe memorizzarlo nel tuo codice sorgente. All'avvio dell'applicazione ( func init()) o quando necessario, è possibile decodificarla al []bytecontenuto originale . Go ha un bel supporto per la codifica Base64 nel encoding/base64pacchetto.

Convertire un file (binario) in base64 stringè semplice come:

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

Memorizza la stringa base64 del risultato nel codice sorgente, ad esempio come file const.

Decodificarlo è solo una chiamata di funzione:

const imgBase64 = "<insert base64 string here>"

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

Conservazione come citato string

Più efficiente dell'archiviazione come base64, ma potrebbe essere più lungo nel codice sorgente archiviare la stringa letterale tra virgolette dei dati binari. Possiamo ottenere la forma quotata di qualsiasi stringa utilizzando la strconv.Quote()funzione:

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

Per i dati binari contenenti valori da 0 a 64, ecco come apparirebbe l'output (provalo su 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:;<=>?"

(Nota che strconv.Quote()aggiunge e antepone una virgoletta.)

Puoi utilizzare direttamente questa stringa tra virgolette nel codice sorgente, ad esempio:

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

È pronto per l'uso, non è necessario decodificarlo; l'annullamento delle quotazioni viene eseguito dal compilatore Go, in fase di compilazione.

Puoi anche memorizzarlo come una fetta di byte se ne hai bisogno in questo modo:

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

c'è un modo per associare un shfile a un eseguibile go?
Kasun Siyambalapitiya

Immagino che i dati dovrebbero essere imgdata nel primo frammento di codice nella sezione "archiviazione come fetta di byte".
logico x 2

1
@deusexmachina Hai ragione, risolto. Il codice nel playground era già corretto.
icza

2

c'è anche un modo esotico: utilizzo il plugin Maven per creare progetti GoLang e consente di utilizzare il preprocessore JCP per incorporare blocchi binari e file di testo nei sorgenti. Nel caso in cui il codice assomigli alla riga sotto ( e alcuni esempi possono essere trovati qui )

var imageArray = []uint8{/*$binfile("./image.png","uint8[]")$*/}

@è possibile associare una directory con un sho un eseguibile come sopra
Kasun Siyambalapitiya

@KasunSiyambalapitiya Associare una directory? Associare un shfile? Non sono sicuro cosa intendi. Se vuoi che tutto in una directory sia incorporato, questo è qualcosa che ho fatto go-bindata. Ad esempio, se inserisco //go:generate $GOPATH/bin/go-bindata -prefix=data/ -pkg=$GOPACKAGE data/un file go (non generato), go generate ./...eseguirò go-bindata nella directory del pacchetto, incorporando tutto in una sottodirectory di dati ma con il prefisso "data /" rimosso.
Marco

1

Come alternativa popolare a quella go-bindatamenzionata in un'altra risposta, mjibson / esc incorpora anche file arbitrari, ma gestisce gli alberi di directory in modo particolarmente conveniente.

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.