美文网首页哲学干货Ruby干货
使用open-uri遇到的问题,这个坑不小

使用open-uri遇到的问题,这个坑不小

作者: AQ王浩 | 来源:发表于2015-03-28 22:10 被阅读1176次

    一、诡异的10kb 的问题

    最近做项目的时候,修改了图片的上传方式。经过缜密的开发和测试,成功上线。
    后台监控newrelic 提示

    undefined method `path' for #<StringIO>
    

    由于上传图片,是非常关键的步骤。大约有 1000rpm,但是为啥报错的仅仅只有那么几个例子呢?

    经过详细的研究和实现。发现open-uri 有一个设置,对 <10kb的文件会将其设置为StringIO类,但是 >10kb 的文件会设置为 Tempfile类。

    其实他的设置还是比较聪明的,我猜想作者的目的是不要让大量的小文件存在。所以设置了这个阀值。

    上例子。

    其中

    http://yannini.qiniudn.com/lessthanten.png 是 <10kb 的文件  
    http://yannini.qiniudn.com/1.png 是 >10kb 的文件
    

    启动irb,

    >> require "open-uri"
    >> open("http://yannini.qiniudn.com/lessthanten.png")
    => #<StringIO:0x00000102b6e920 @base_uri=#<URI::HTTP:0x00000102b6eba0 URL:http://yannini.qiniudn.com/lessthanten.png>, @meta={"date"=>"Sat, 28 Mar 2015 13:43:04 GMT", "server"=>"nginx/1.4.4", "content-type"=>"image/jpeg", "content-length"=>"8157", "accept-ranges"=>"bytes", "access-control-allow-origin"=>"*", "access-control-max-age"=>"2592000", "cache-control"=>"public, max-age=31536000", "content-disposition"=>"inline; filename=\"lessthanten.png\"", "content-transfer-encoding"=>"binary", "etag"=>"\"Fg3HN9hK3Hy68nGT99MOSdtaafDx\"", "x-log"=>"mc.g;IO:1", "x-reqid"=>"BVsAAE70cUNUrc8T", "x-whom"=>"nb990", "x-qiniu-zone"=>"0", "age"=>"1", "x-via"=>"1.1 tzh55:8104 (Cdn Cache Server V2.0), 1.1 bjdxtck17:8 (Cdn Cache Server V2.0)", "connection"=>"keep-alive"}, @metas={"date"=>["Sat, 28 Mar 2015 13:43:04 GMT"], "server"=>["nginx/1.4.4"], "content-type"=>["image/jpeg"], "content-length"=>["8157"], "accept-ranges"=>["bytes"], "access-control-allow-origin"=>["*"], "access-control-max-age"=>["2592000"], "cache-control"=>["public, max-age=31536000"], "content-disposition"=>["inline; filename=\"lessthanten.png\""], "content-transfer-encoding"=>["binary"], "etag"=>["\"Fg3HN9hK3Hy68nGT99MOSdtaafDx\""], "x-log"=>["mc.g;IO:1"], "x-reqid"=>["BVsAAE70cUNUrc8T"], "x-whom"=>["nb990"], "x-qiniu-zone"=>["0"], "age"=>["1"], "x-via"=>["1.1 tzh55:8104 (Cdn Cache Server V2.0), 1.1 bjdxtck17:8 (Cdn Cache Server V2.0)"], "connection"=>["keep-alive"]}, @status=["200", "OK"]>
    >> open("http://yannini.qiniudn.com/lessthanten.png").class
    => StringIO
    
    
    >> open("http://yannini.qiniudn.com/1.png")
    => #<Tempfile:/var/folders/mj/q33ysmhn02v_xr430ftz6mnw0000gn/T/open-uri20150328-31267-1qyc4wr>
    >> open("http://yannini.qiniudn.com/1.png").class        
    => Tempfile
    

    很明显,两个文件最终生成的类不一致。
    接着往下看,上传图片时,需要使用图片的真实路径。所以需要调用file.path 方法,但是 StringIO没有path方法,导致报错。

    >> open("http://yannini.qiniudn.com/1.png").path
    => "/var/folders/mj/q33ysmhn02v_xr430ftz6mnw0000gn/T/open-uri20150328-31267-oyxvdv"
    >> open("http://yannini.qiniudn.com/lessthanten.png").path
    NoMethodError: undefined method `path' for #<StringIO:0x00000101b9d168>
        from (irb):16
        from /Users/wanghao/.rvm/rubies/ruby-2.1.1/bin/irb:11:in `<main>'
    >>
    

    二、解决方式

    rails script目录下,新建一个 reset_open_uri.rb

    if defined?(OpenURI) && OpenURI::Buffer.const_defined? (:StringMax)
      OpenURI::Buffer.send('remove_const', :StringMax)
      OpenURI::Buffer.send('const_set', :StringMax, 0)
    end
    

    然后在需要启动的开发或生产环境中,引入该配置即可

    require(File.join(Rails.root, 'script/reset_open_uri.rb'))
    

    这样,任何大小的文件都open时候最终都生成 Tempfile

    三、清除大量的小图片

    由于上述设置,大量的小图片生成。导致服务器的硬盘空间不足。
    所以最后需要写一个定时任务,手动删除 cd /var/folders/mj/q33ysmhn02v_xr430ftz6mnw0000gn/T/ 下的关联文件。

    参考

    Undefined Method 'path' For StringIO in Ruby
    Why does Ruby open-uri's open return a StringIO in my unit test, but a FileIO in my controller?

    相关文章

      网友评论

      本文标题:使用open-uri遇到的问题,这个坑不小

      本文链接:https://www.haomeiwen.com/subject/qjmnxttx.html