Подтвердить что ты не робот

Каков наилучший способ связывания статических ресурсов в программе Go?

Я работаю над небольшим веб-приложением 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 запроса и обслуживать их как таковые.

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

Если есть более подходящие теги, чем , не стесняйтесь добавлять их или сообщать мне.

4b9b3361

Ответ 1

Пакет go-bindata выглядит так, как будто он вас заинтересовал.

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

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

Ответ 2

Встраивание текстовых файлов

Если мы говорим о текстовых файлах, они могут быть легко встроены в сам исходный код. Просто используйте обратные кавычки, чтобы объявить 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 типа []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 Playground):

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

Сохранение в виде string base64

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

Преобразование (двоичного) файла в string base64 так же просто, как:

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

Ответ 3

также есть какой-то экзотический способ - я использую maven plugin для создания проектов GoLang и позволяет использовать препроцессор JCP для встраивания двоичных блоков и текстовых файлов в источники. В коде кода просто выглядит строка ниже (и пример можно найти здесь)

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

Ответ 4

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