如何在ruby中有效地将文件拆分为任意字节范围



我需要将一个大文件上传到第三方服务。这个第三方服务给了我一个url和字节范围的列表:

requests = [
{url: "https://.../part1", from: 0, to: 20_000_000},
{url: "https://.../part2", from: 20_000_001, to: 40_000_000},
{url: "https://.../part3", from: 40_000_001, to: 54_184_279}
]

我使用httpx-gem上传数据,:body选项可以接收IOEnumerable对象。我想以一种有效的方式分割和上传区块。这就是为什么我认为我应该避免向磁盘写入块,也避免将整个文件加载到内存中。我想最好的选择是某种";lazy Enumerable";但我不知道如何编写part函数来返回这个IOEnumerable对象。

file = File.open("bigFile", "rb")
results = requests.each do |request|
Thread.start { HTTPX.post(request[:url]), body: part(file, request[:from], request[:to]) }
end.map(&:value)
def part(file, from, to)
# ???
end

为每个"字节范围";将是让part函数处理文件的打开:

def part(filepath, from, to = nil, chunk_size = 4096, &block)
return to_enum(__method__, filepath, from, to, chunk_size) unless block_given?
size = File.size(filepath)
to = size-1 unless to and to >= from and to < size
io = File.open(filepath, "rb")
io.seek(from, IO::SEEK_SET)
while (io.pos <= to)
size = (io.pos + chunk_size <= to) ? chunk_size : 1 + to - io.pos
chunk = io.read(size)
yield chunk
end
ensure
io.close if io
end

警告:区块大小计算可能错误,我稍后会检查(我必须照顾我的孩子(

注意:您可能需要改进此功能,以确保始终读取完整的物理HDD块(或其倍数(,因为这将大大加快IO速度。from不是物理HDD块的倍数时,您会出现错位

part函数现在在没有块的情况下调用时返回Enumerator

part("bigFile", 0, 1300, 512)
#=> #<Enumerator: main:part("bigFile", 0, 1300, 512)

当然,你可以直接用一个块来称呼它:

part("bigFile", 0, 1300, 512) do |chunk|
puts "#{chunk.inspect}"
end
IO.read("bigFile", 1000, 2000)

将从偏移量2000开始读取1000字节。Ruby从零开始计数,所以我认为

IO.read("bigFile", 20_000_000, 0) #followed by
IO.read("bigFile,20_000_000,20_000_000) #not 20_000_001

将是正确的。无记账:

f = File.open("bigFile")
partname = "part0"
until f.eof? do
partname = partname.succ
chunk = f.read(20_000_000)
#do something with chunk and partname
end
f.close

最新更新