ror下文件下载是通过send_file完成的,但是如果使用多线程并支持断点续传的客户端(FlashGet等),send_file是不能正确工作的,原因在于,send_file函数没有对HTTP协议的Range头做相应的支持,并且也不支持HTTP/1.1 206 Partial Content相应
我修改了send_file函数,并做了一个plugin 将起解压到vendor/plugins/就可以了
(必要的地方我都写了注释,欢迎大家提出建议和意见)
module ActionController
module Streaming
protected
def send_file(path, options = {})
raise MissingFile, "Cannot read file #{path}" unless File.file?(path) and File.readable?(path)
options[:length] ||= File.size(path) # 文件长度
options[:filename] ||= File.basename(path) # 文件名称
options[:from] ||= 0 # 首偏移量(从哪里开始下载)
options[:to] ||= options[:length] # 结束位置
m_send_file_headers! options # 设置必要的 http 头
@performed_render = false
if options[:stream]
render :status => options[:status], :text => Proc.new { |response, output|
logger.info "Streaming file #{path}" unless logger.nil?
len = options[:buffer_size] || 8192 # 原来的实现是4K, 不过APUE 上说,8K 要好一些,所有我调整了一下
File.open(path, 'rb') do |file|
file.seek(options[:from].to_i, IO::SEEK_SET) if options[:status] == 206 # 如果是多线程下载,则将流定位到首偏移量位置,从此处开始传输
while buf = file.read(len)
# 这个可以优化一下,只要从from 发送到 to 字节就可以了,但我测试过,不进行这个判断是可以的,客户自己计算并保存问下载的起始位置
# 不过我觉得最好还是要给只发送 from 到 to 字节, 虽然有点麻烦
# 这个功能以后在实现吧
output.write(buf)
end
end
}
else
logger.info "Sending file #{path}" unless logger.nil?
File.open(path, 'rb') { |file| render :status => options[:status], :text => file.read }
end
end
private
def m_send_file_headers!(options)
options.update(DEFAULT_SEND_FILE_OPTIONS.merge(options))
[:length, :type, :disposition].each do |arg|
raise ArgumentError, ":#{arg} option required" if options[arg].nil?
end
disposition = options[:disposition].dup || 'attachment'
disposition <<= %(; filename="#{options[:filename]}") if options[:filename]
# 先输出一些通用的HTTP头
headers.update(
'Content-Type' => options[:type].strip, # fixes a problem with extra '\r' with some browsers
'Content-Disposition' => disposition,
'Content-Transfer-Encoding' => 'binary'
)
# 判断是否存在Range头,并使用正则表达式匹配得到from 和 to
# 如果匹配成功,则表示客户端使用多线程下载,同时一定要将http status 设置为 206
if request.env['HTTP_RANGE'] =~ /bytes=(\d+)-(\d*)/ then # 注意后一个\d*,有一些多线程客户端并不完全符合http Range 头的要求(例如FlashGet)
options[:from] = $1
options[:to] = $2 unless $2.nil? # 如果客户端不标准,就将 to 设置为文件末尾
# 匹配成功,设置status 为 206
options[:status] = 206
# 一下3个http 头仅在多线程下载是有用
headers['Accept-Ranges'] = 'bytes'
headers['content-Range'] = "bytes #{options[:from]}-#{options[:to]}/#{options[:length]}" # 格式为 bytes from-to/total
headers['Content-Length'] = options[:to].to_i - options[:from].to_i + 1 # 注意 在多线程下载下,Content-Length 为传输的实际字节数(从0开始算起,所有要+1)
else
# 非多现场下载
options[:status] = 200 # 请求正常标志
headers['Content-Length'] = options[:length] # 非多线程下载下,Content-Length为文件长度
end
headers['Cache-Control'] = 'private' if headers['Cache-Control'] == 'no-cache'
end
end
end
用法和老send_file一样
class FileController < ApplicationController
def download
#logger.debug request.env['Range'];
# request.env.each do |key, value|
# logger.debug key + '--------' + value
# end
send_file 'public/jdk.tgz'
end
end
分享到:
- 2007-09-11 13:49
- 浏览 3156
- 评论(3)
- 论坛回复 / 浏览 (2 / 4458)
- 查看更多
相关推荐
ror中文资料ror中文资料
ror实例ror实例ror实例
RoR性能优化经验谈,牛人对ror的优化,值得大家阅读
NULL 博文链接:https://xuxiangpan888.iteye.com/blog/266696
我自己再开发时的笔记
ROR 架构介绍及了解
之前本人在windows 上搭建ROR环境,屡屡受挫,上网查资料、加入ruby on rails的qq群,关注ruby社区等,没有得到一套完整的正确的方法,很多朋友说不要在windows 弄ROR,很费力。的确很费力,后来在朋友的帮助下,...
基于RoR的博客系统,代码风格简单清晰,前后太完善,适合初学者。
神经网络ror resenet模型 cifar10准确率89% cifar100准确率72%
破解版本的JAR包,放到本地磁盘,ECLIPUS直接用,到JVM设置直接加 -noverify -javaagent:D:\javarebel.jar
ROR-ActiveRecord持久层框架的Java实现
RoR选题方向—源代码
家蚕Ror2基因的克隆与表达分析,刘伟,陈菲,Ror家族属于受体酪氨酸激酶中的孤儿受体,在进化上非常保守。本研究克隆了家蚕Ror2基因BmRor2的ORF序列,BmRor2基因的ORF为1924 bp,编码638
ROR环境 Ruby version 1.9.3 (java) RubyGems version 1.8.24 Rack version 1.4 Rails version 3.2.12 JavaScript Runtime therubyrhino (Rhino) Active Record version 3.2.12 Action Pack version 3.2.12 ...
mysqlDriver4ror.rarmysqlDriver4ror.rarmysqlDriver4ror.rarmysqlDriver4ror.rar
研究机构注册(ROR)API ROR API允许检索,搜索和过滤在ROR中建立索引的组织。 结果以JSON返回。 单个组织记录由以下JSON结构表示: { "id":"https://ror.org/013cjyk83", "name":"PSL Research University", "email...
excel lib ror ruby
asp.net 支持断点续传,大文件上传
Source.Insight.v3.50.0027-ROR.rar
使用ProgramR 和AIML 基于RoR的自动问答机器人。