最近在实现一款小程序时,使用到了视频在线播放的功能,由于资金紧张,所以没有采用第三方云存储的方案,而是将视频文件通过文件上传的功能保存在了自己服务器的磁盘上,而查看时,使用iris的sendFile方法将视频文件输入到response中给前台,而这一方法在chrome浏览器中使用video的方法测试没有任何问题,然后发布小程序,在安卓手机上查看也没发现问题,但是在safari上或者iphone手机上查看,则无法播放视频文件,之后在网上查找了一些方法,发现时safari浏览器本身对视频文件的访问策略有自己的方式(通过HttpRequest的Header中Range属性分段获取视频文件),而且网上也给出了相关的解决方法,有java和php的版本,但是没有go语言版本,所以我将自己参考网上的方法使用go+iris的实现方式分享给大家,希望能帮助到大家。代码如下:
func (c *AttachController) GetVideoBy(id uint) {
if id <= 0 {
return
}
attach, err := c.Service.GetAttachById(id) // 获取数据库中文件的记录
if err != nil {
c.Ctx.Application().Logger().Error(err)
}
file := attach.FilePath // 获取文件路径
f, _ := ioutil.ReadFile(file) // 根据文件路径获取文件字节数组
var reqRange = c.Ctx.Request().Header.Get("Range") // 获取请求头的Range属性值
var reqBlockRange []string
if reqRange != "" && strings.HasPrefix(reqRange, "bytes=") {
reqBlockRange = strings.Split(strings.Split(reqRange, "=")[1], "-")
c.Ctx.StatusCode(206) // 设置返回状态码,206表示为断点续传
c.Ctx.ResponseWriter().Header().Set("status", "206")
}
fileSize := len(f) // 计算文件字节长度
startPosition := 0 // 开始读取的位置
endPosition := 0 // 结束读取的位置
if reqBlockRange != nil {
//safari
startPosition, _ = strconv.Atoi(reqBlockRange[0])
if len(reqBlockRange) > 1 && reqBlockRange[1] != "" { // 如果Range有截止位置,则将截止位置赋值
tmp, _ := strconv.Atoi(reqBlockRange[1])
endPosition = tmp
} else { // 否则读取全部文件
// chrome 等
endPosition = fileSize - 1
}
}
if reqBlockRange != nil { // 如果safari浏览器时
if len(reqBlockRange) > 1 && reqBlockRange[1] != "" {
c.Ctx.ResponseWriter().Header().Set("accept-ranges", "bytes")
c.Ctx.ResponseWriter().Header().Set("access-control-allow-methods", "HEAD, GET, OPTIONS")
c.Ctx.ResponseWriter().Header().Set("cache-control", "public, max-age=30726563")
c.Ctx.ResponseWriter().Header().Set("Content-Type", "video/mp4") // 设置内容类型
c.Ctx.ResponseWriter().Header().Set("Last-Modified", time.Now().String())
c.Ctx.ResponseWriter().Header().Set("Connection", "keep-alive")
c.Ctx.ResponseWriter().Header().Set("content-range", fmt.Sprintf("bytes %d-%d/%d", startPosition, endPosition, fileSize)) // 设置本次读取的范围
c.Ctx.ResponseWriter().Header().Set("Content-Length", fmt.Sprintf("%d", endPosition-startPosition+1)) // 设置返回的内容长度
c.Ctx.ResponseWriter().Write(f[startPosition : endPosition+1]) // 截取文件对应的字节位置写入response
} else { // chrome 等
c.Ctx.StatusCode(200)
c.Ctx.SendFile(file, attach.FileDisName)
}
} else { // chrome 等
c.Ctx.StatusCode(200)
c.Ctx.SendFile(file, attach.FileDisName)
}
}
有疑问加站长微信联系(非本文作者)