当nginx遇到safari

  铺垫:

  楼主本身是一个做android 开发的代码狗,去年毕业之前在一家外包公司做了一段时间的Java开发,近期公司业务拓展,因为面试来的时候,跟现在的大哥吹牛逼,说自己做过服务端开发,所以就被搞来做服务端开发。这中间遇到很多坑,挨着坑一个个走过来,很累,收获也很丰富。

  技术介绍:

 1 : openresty   :https://github.com/openresty/lua-nginx-module#nginx-log-level-constants

    2 : mongoDB Driver: https://github.com/aaashun/lua-resty-mongol

 3 :  openresty.upload :  https://github.com/openresty/lua-resty-upload 

        主要需求:

 1 : 音频上传,存储(mongo)

    2 : 音频下载 

  实现:

  1 音频上传服务:

  方案:选用opresty.upload接受上传音频数据,使用mongodb来存储音频和必要的基本信息

  代码:

local form  = upload:new(4096)
if not form then
      ngx.log(ngx.ERR,"failed to new upload :",err)
      ngx.say(json.error_json("401","failed request"))--失败的请求
      ngx.exit(ngx.HTTP_INTERNAL_SERVER_ERROR)
end
  
form:set_timeout(1000)
  
local buf = {}
  --开始循环读取文件
while true do
  local typ , res , err = form:read()
  if not typ then
          ngx.say("failed to read: ", err)
          return
   end
   if typ == 'header' then
          if res[1] ==  'Content-Disposition' then
              file_name = get_fileName(res[1],res[2])
              --ngx.say('file_name:'..file_name)
   end
  
        if not file_name then
              ngx.say(json.error_json('401','failed to get file name'))
              ngx.log(ngx.ERR,"failed to get file name", file_name)
              return
        end
  
   elseif typ == 'part_end' then
          --ngx.say(json.json(buf))
   elseif typ == 'eof' then
          break
   elseif typ == 'body' then
          table.insert(buf, res)
          -- file = file..res
    end
  end

ps :代码写到这里我就已经完成接受文件的过程了,剩下的就是想办法把音频写到mongo数据库中,代码实现如下:

local conn = mongol.get_conn() --获取数据库链接
local db =  mongol.get_db(conn,'数据库名称')

if not db then
     ngx.log(ngx.ERR , '错误信息')
     ngx.say(json.error_json('401','get db error'))
     return
end
 
 
local ff = fake_file:new(table.concat(buf))
local fs = db:get_gridfs('upload')
local _id = is_exis(fs) --这里是楼主自己简单实现的一个6位随机ID方法
 
local n,err = fs:insert(ff, { number = _id,name = file_name } , false)
 
mongol.close(conn)
 
if not n then
     ngx.log(ngx.ERR , 'failed to insert table to col')
     ngx.say(json.error_json('402','failed to insert db'))
end
 
local res = { url = config.IP..'m/'.._id}
ngx.say(json.json(res))

看到这里大家是不是都很迷茫,我的mongol到底是个什么东西,其实这里的mongol只是对上面提到的mongoDB Driver的一个简单封装,其实这里也都没必要这么写,但是处于之前写Java代码的习惯,深深被面向对象的思想所影响,还是做了一个单独的lua文件,代码如下:

local mongol =  require("resty.mongol")
local conf = require("init.config")

local _M = {}

function _M.get_conn()

       local conn = mongol:new()

       ok , err = conn:connect(conf.MONGO_HOST, conf.MONGO_PORT)
       if not ok then
                 ngx.log(ngx.ERR,"mongo connect err :"..err)
              return nil
        end
        if not ok then
               ngx.log(ngx.ERR , "mongo set timeout err : "..err)
               return nil
        end
       return  conn
 end
 
function _M.close(conn)
 
       return conn:set_keepalive(60000, 200)

end

function _M.get_db (conn,database_name)

    return conn:new_db_handle(database_name)

end

return _M

这样就可以在你的mongo数据中查询到上传的音频数据了,推荐ubuntu下面使用robomongo作为数据库可视化工具,写到这里,其实已经实现了音频的上传服务并写入到mongo数据库中,是不是很清晰,代码也傻瓜,其实做到这里大家能不能体会到openresty和mongo的强大,所有的逻辑都很清晰,也不用写一句sql语句。ps:这里吐槽一下开源中国的博客,图片上传居然限制200k,所以就不上传数据库截图了~

2 音频下载服务:

  写到这里,楼主有想要吐槽的地方了,开始的时候,楼主只是简单实现了数据库查询和返回一堆二进制字符串,但是测试下来发现在Chrome,Firefox和Android设备都表现良好,但是偏偏在IOS设备中没有声音!!!一开始楼主以为是自己写的HTML标签有问题(使用H5中的audio标签来播放音频),就一直查啊查,因为确实发现safari对标签上面的支持有问题,就更加坚定了HTML的问题,可是不管我怎么去改HTML都没有任何毛用(我又想吐槽,当时无线后悔上学的时候老师讲HTML的时候为毛不好好学习,就知道睡觉,真是书到用时方恨少,艺多不压身!在这里给还在上学的童鞋尤其是念软件工程的童鞋洗个脑,一定不能轻视任何一门课程,楼主血淋淋的教训在这里),因为这一开始就偏离了正确的道路,后来不得不去求助公司的大哥,就这样,我和大哥因为这个问题搞到了半夜三点多,热了个粽子还差点搞了场火灾。。。

  关键帖子:http://stackoverflow.com/questions/3397241/does-iphone-ipad-safari-require-accept-ranges-header-for-video

     知识普及:https://www.safaribooksonline.com/library/view/http-the-definitive/1565925092/re05.html

       总结了一下,苹果的safari浏览器在解析H5 audio标签中要求服务器必须支持byte-range requests 服务,也就是说要求必须要Header中明确要有Range字段,这样safari浏览器才能解析到音频!!!

  吐槽结束,直接贴代码:

local mongol = require("db.mongo_pool")

local headers = ngx.req.get_headers()
local is_byte_range_request, range_from, range_to
if headers.range then
        is_byte_range_request = true
        range_from, range_to = string.match(headers.range, "bytes=(%d+)-(%d+)")
        range_from = tonumber(range_from)
        range_to = tonumber(range_to)
end

local id = string.sub(ngx.var.uri,string.find(ngx.var.uri,'m')+2,string.len(ngx.var.uri)-6) --截取id
local conn = mongol.get_conn()
local db =  mongol.get_db(conn,'db_wechat')
if not db then
     ngx.log(ngx.ERR , "failed to get db,check upload_data.lua")
     ngx.say(json.error_json('401','get db error'))
     return
end

local tb_file = db:get_col('upload.files'):find_one({number = id})
local grifs_file = db:get_gridfs('upload'):find_one({number = id})
local length = tb_file.length
local name = tb_file.name

if not grifs_file then
       ngx.say(json.error_json('402','audio id not found!'))
       return
end

ngx.header.content_type = 'audio/' .. name:match(".+%.(%w+)$")

if is_byte_range_request then
      local data = grifs_file:read(length, 0) -- offset has bug
      data = string.sub(data, range_from + 1, range_to + 1)
   ngx.status = ngx.HTTP_PARTIAL_CONTENT
      ngx.header.Content_Length = range_to - range_from + 1
      ngx.header.Content_Range = 'bytes ' .. range_from .. '-' .. range_to .. '/' .. length
   local ok, err = ngx.print(data)
      local ok, err = ngx.flush(true)
else
      local data = grifs_file:read(length, 0)
      ngx.header.Accept_Ranges = 'bytes'
      ngx.print(data)
end

mongol.close(conn)

如上代码主要是公司的大哥给实现的,在这里再次感谢一下大哥的辛苦劳动!大哥说这里的grifs_file:read()方法有bug,让我提一个补丁到他的github上面的分支上,目前我还还在查这个bug,这篇文章写完后就会去老老实实改bug,阅读这篇文章的童鞋请放心使用上面那个mongo Driver  github上面的分支代码哈,补丁我会提上去。 文章贴的代码已经是安全的,可以正常使用的,因为我们是一次性读取的二进制数据,又按需截取的数据。

 

    总结:开始的时候,刚刚接手nginx这个服务器,之前虽然做过一段时间服务端开发,但是还是用的老旧的Apache服务器,J2EE和mysql数据库,当刚刚接触到openresty这个开源组织,楼主其实挺无知的,也没有接触过lua这种轻量级的脚本语言,现在看看最开始的时候其实觉得听无知的,借用早上看到广告牌牌上面的一句话:不登高山不知天之大,不临深谷不知地之厚。楼主觉得做技术的童鞋都时刻应该保持谦虚谨慎的态度,永远不知饥渴的信仰,勇往直前的信念!致敬技术,热爱技术。后面有时间我会再更新几篇文章,关于nginx微信公众号服务端开发和android地图相关业务开发。有想交流的童鞋可以关注楼主微博: http://weibo.com/2522671414/profile?rightmod=1&wvr=6&mod=personinfo&is_all=1 

转载于:https://my.oschina.net/u/2278066/blog/687374

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值