我有一个用 go 编写的网络服务器,我正在提供来自不同来源(本地、其他服务器、S3)的一些音频文件。我想为此文件启用部分内容,以便 HTML 音频标签能够搜索和循环。
我怎样才能做到这一点?我知道http
包ServeContent
功能可以做到这一点,但我怎样才能通过自己提供文件来做到这一点?我需要这样做,以便我可以使用相同的处理程序提供来自不同来源的文件。
我有一个用 go 编写的网络服务器,我正在提供来自不同来源(本地、其他服务器、S3)的一些音频文件。我想为此文件启用部分内容,以便 HTML 音频标签能够搜索和循环。
我怎样才能做到这一点?我知道http
包ServeContent
功能可以做到这一点,但我怎样才能通过自己提供文件来做到这一点?我需要这样做,以便我可以使用相同的处理程序提供来自不同来源的文件。
提供部分内容并非易事。有关介绍,请参阅Wikipedia 上的字节服务。您必须处理特定的状态代码和标头(请求和响应),这并不难,但您不应该自己浪费时间。
如果要提供(或从中提供服务)的内容是一个文件,您可以http.ServeFile()
像您提到的那样使用它来处理提供部分内容(范围请求)。
如果要提供的内容不是文件,那么http.ServeContent()
您的朋友是:
func ServeContent(w ResponseWriter, req *Request,
name string, modtime time.Time, content io.ReadSeeker)
是的,它还处理提供部分内容(范围请求):
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()
调用它是为了填充传递的切片(以提供实际内容)。请注意,这些方法可能会被多次调用,因此您必须跟踪您在内容(源)中的位置。如果您选择走这条路,请阅读链接接口的文档,以确保您符合接口的“通用合同”以避免意外错误。