Rails carrierwave+gridfs 怎样实现强制下载?

bindiry · January 20, 2012 · Last by bindiry replied at February 04, 2012 · 3644 hits

我用如下代码,想实现强制下载,但总是报错: Cannot read file /uploads/video/4f03eae211aa2b21b1000fa5/aaa.mp4

view:

<%= link_to "下载", { :action => "download", :id => @video.id } %>

controller:

def download
  @video = Video.find(params[:id])
  if @video.blank?
    render_404
  end
  @video.download = @video.download + 1
  @video.save
  send_file @video.file_url, :type => 'application/octet-stream', :disposition => 'inline'
end

而直接在 view 里

<%= link_to "下载", @video.file_url %>

是可以访问的。 请教同学们,问题出在哪儿呢?

在 stackoverflow 上找到一个问题和我几乎一样,但没有被解决。。。

http://stackoverflow.com/questions/8558285/carrierwave-fogs3-letting-the-users-to-download-the-file

@video.file_url返回的网址绝对路径,send_file 要的是磁盘绝对路径 所以你应该写 send_file "#{Rails.root}/public#{@video.file_url}"

#2 楼 @ywencn 谢谢回复。 磁盘绝对路径和 url 绝对路径都试过,同样的错误,怀疑是不是用 send_file 方法时,GridfsController 里的 serve 没有正确触发,所以没成功从 gridfs 里读出文件来。

把 GridfsController 里的处理方法写进 download 试试。

已解决,我的代码如下:

ApplicationController 里写了个 download_file 方法,便于重用:(处理方法和 GridfsController 里的 serve 方法一致,只是用 send_file_headers 来处理了一下文件名)

def download_file(path)
  gridfs_path = path.gsub("/uploads/", "uploads/")
  begin
    options = {:filename => File.basename(path)}
    send_file_headers! options
    gridfs_file = Mongo::GridFileSystem.new(Mongoid.database).open(gridfs_path, 'r')
    self.response_body = gridfs_file.read
    self.content_type = gridfs_file.content_type
  rescue Exception => e
    self.status = :file_not_found
    Rails.logger.debug { "#{e}" }
    self.content_type = 'text/plain'
    self.response_body = 'file not found'
    raise e
  end
end

然后就是 VideosController 里的 download 方法来调用 download_file:

def download
  @video = Video.find(params[:id])
  if @video.blank?
    render_404
  end
  # send_file @video.file_url, :type => 'application/octet-stream', :disposition => 'attachment'
  @video.download = @video.download + 1
  @video.save
  download_file(@video.file_url)
end

view 里:

<%= link_to "下载", { :action => "download", :id => @video.id } %>
You need to Sign in before reply, if you don't have an account, please Sign up first.