Який найкращий спосіб зібрати статичні ресурси в програмі Go? [зачинено]


100

Я працюю над невеликим веб-додатком у Go, який призначений для використання в якості інструменту на машині розробника для налагодження їхніх додатків / веб-служб. Інтерфейс програми - це веб-сторінка, яка включає не тільки HTML, але і деякий JavaScript (для функціональності), зображення та CSS (для стилізації). Я планую відкрити цей додаток, тому користувачі повинні просто мати змогу запустити Makefile, і всі ресурси підуть туди, куди потрібно. Однак я також хотів би мати можливість просто розподілити виконуваний файл з якомога меншою кількістю файлів / залежностей. Чи є хороший спосіб поєднати HTML / CSS / JS з виконуваним файлом, тому користувачі повинні завантажувати та турбуватися лише про один файл?


Зараз у моїй програмі подання статичного файлу виглядає приблизно так:

// 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 запиту та обслуговувати їх як такі.

Якщо це не вдало зробити це, або я гаю неправильне дерево, намагаючись це зробити, дайте мені знати. Я просто подумав, що кінцевий користувач оцінить якомога менше файлів, якими можна керувати.

Якщо є більш відповідні теги, ніж , не соромтесь додавати їх або повідомте мене.



Я насправді просто думав саме таке питання сьогодні. Я можу дослідити це рішення - використовувати go generateневелику утиліту командного рядка (упаковану з моїм вихідним кодом) для перетворення файлів у []byteфрагменти, які вбудовані як змінні в код, аналогічно тому, як stringerце робиться (див. Blog.golang.org / генерувати ).
Ральф

Відповіді:


76

Пакет go-bindata виглядає так, що це може бути те, що вас цікавить.

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

Це дозволить вам перетворити будь-який статичний файл у виклик функції, який можна вбудувати у ваш код, і поверне байтовий фрагмент вмісту файлу при виклику.


8
Оновлення цього видається дивно корисним у моєму випадку, але я все одно це зроблю: p Для запису, проте, це не пакет, а інструмент командного рядка.
jimt

Тільки для запису, це шлях, який я пройшов зі своїм проектом. У якийсь момент @jimt представив нові функції, щоб зробити речі більш зручними для користувачів, але більше не забезпечував потрібну деталізацію, тому я написав власний інструмент, який має менше функцій, але призначений для мого використання (я використовую цей інструмент як своєрідний преамбула до процесу збирання): github.com/jimmysawczuk/go-binary
Jimmy Sawczuk

37

Вставлення текстових файлів

Якщо ми говоримо про текстові файли, вони легко вбудовуються у сам вихідний код. Просто використовуйте зворотні лапки, щоб оголосити 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 .

Якщо ви не хочете використовувати для цього бібліотеку сторонніх розробників, ось простий фрагмент коду, який читає двійковий файл, і виводить вихідний код Go, який оголошує змінну типу, []byteяка буде ініціалізована з точним вмістом файлу:

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 ):

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

Зберігання як основи64 string

Якщо файл не "занадто великий" (більшість зображень / піктограм відповідає), є й інші життєздатні варіанти. Ви можете конвертувати вміст файлу в Base64 stringі зберігати його у вихідному коді. Під час запуску програми ( func init()) або за потреби ви можете розшифрувати його до оригінального []byteвмісту. Go має гарну підтримку кодування Base64 в encoding/base64пакеті.

Перетворення (бінарного) файлу в base64 stringпросте, як:

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

Збережіть результат base64 рядок у вихідному коді, наприклад, як const.

Розшифровка - це лише один виклик функції:

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

чи є спосіб прив’язати shфайл до виконуваного файлу?
Касун Сіямбалапітія

Я здогадуюсь, що дані мають бути імгдата в першому фрагменті коду в розділі "Зберігання як байтовий фрагмент".
логічно x 2

1
@deusexmachina Ви праві, виправте це. Код на майданчику вже був правильним.
ікза

2

також є якийсь екзотичний спосіб - я використовую плагін Maven для створення проектів GoLang, і це дозволяє використовувати препроцесор JCP для вбудовування бінарних блоків та текстових файлів у джерела. У коді випадку просто виглядайте як рядок нижче ( а приклад можна знайти тут )

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

@ чи можна прив’язати каталог, що має shабо
виконаний

@KasunSiyambalapitiya Прив’язати каталог? Зв'язати shфайл? Не впевнений, що ти маєш на увазі. Якщо ви хочете, щоб все в каталозі було вбудовано, я це зробив go-bindata. Наприклад, якщо я вставлю (негенерований //go:generate $GOPATH/bin/go-bindata -prefix=data/ -pkg=$GOPACKAGE data/) файл go, go generate ./...запустить go-bindata в dir пакета, вбудувавши все у субдіар даних, але видалений префікс 'data /'.
Марк

1

Як популярна альтернатива, go-bindataзгадана в іншій відповіді, mjibson / esc також вбудовує довільні файли, але особливо зручно обробляє дерева каталогів.

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.