Как обслуживать частичный контент http с помощью Go?

У меня есть веб-сервер, написанный на go, и я обслуживаю некоторые аудиофайлы из разных источников (локальных, других серверов, S3). Я хочу включить частичное содержание для этих файлов, чтобы аудио-теги HTML могли выполнять поиск и цикл.

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

1 ответ

Решение

Обслуживание частичного контента нетривиально. См. Байт, обслуживающий Википедию, для ознакомления. Вы должны обрабатывать определенные коды состояния и заголовки (как запрос, так и ответ), что не так уж сложно, но вы не должны тратить свое время, делая это самостоятельно.

Если контент для обслуживания (или подачи из) является файлом, вы можете использовать http.ServeFile() как вы упомянули, обрабатывает частичное содержимое (запросы Range).

Если контент для обслуживания не представлен в виде файла, то http.ServeContent() твой друг:

func ServeContent(w ResponseWriter, req *Request,
    name string, modtime time.Time, content io.ReadSeeker)

И да, он также обрабатывает частичный контент (запросы Range):

Основным преимуществом ServeContent по сравнению с io.Copy является то, что он правильно обрабатывает запросы Range, устанавливает тип MIME и обрабатывает запросы If-Modified-Since.

Все, что вам нужно сделать, это предоставить io.ReadSeeker "просмотр" вашего контента, это необходимо для того, чтобы реализация могла "перейти" к той части, которую запрашивает клиент, той части, которая должна быть обслужена. Вы можете спросить: как это сделать?

bytes Пакет содержит тип, который реализует io.ReadSeeker: bytes.Reader,

Так, например, если у вас есть контент как []byte Вы можете получить io.ReadSeeker как это:

var content []byte
// fill content
r := bytes.NewReader(content)

А что, если у вас нет всего контента как []byte? Одним из вариантов является предоставление значения вашего собственного типа, которое реализует io.ReadSeeker,

io.ReadSeeker является:

type ReadSeeker interface {
    Reader
    Seeker
}

io.Reader содержит один метод:

Read(p []byte) (n int, err error)

io.Seeker также содержит один метод:

Seek(offset int64, whence int) (int64, error)

Ваш контент доступен где-то, каким-то образом, вы знаете, как. Seek() вызывается, чтобы вы знали, какая часть (позиция) требуется от вашего контента, и Read() вызывается, так что вы можете заполнить переданный фрагмент (для предоставления фактического содержимого). Обратите внимание, что эти методы могут вызываться несколько раз, поэтому вам нужно отслеживать, где вы находитесь в своем контенте (источнике). Если вы решите пойти по этому пути, пожалуйста, прочитайте документацию по связанным интерфейсам, чтобы убедиться, что вы выполняете "общий контракт" интерфейсов, чтобы избежать неожиданных ошибок.

Другие вопросы по тегам