在 Ruby 中下载 Azure Blob 时出现 NoMemoryError
NoMemoryError when downloading Azure Blob in Ruby
环境:
- Windows 10 x64
- Ruby 2.1.0 32 位
- 大厨 12.12.15
- 蔚蓝 Gem 0.7.9
- Azure 存储 Gem 0.12.1.preview
我正在尝试从容器中下载约 880MB 的 blob。当我这样做时,它会在 Ruby 进程大小达到 ~500MB 后抛出以下错误:
C:/opscode/chefdk/embedded/lib/ruby/2.1.0/net/protocol.rb:102:in `read': failed to allocate memory (NoMemoryError)
我已经在 Ruby 内部和外部以及 Azure gem 和 Azure-Storage gem 上进行了尝试。结果与所有四种组合相同(Chef 中的 Azure、Ruby 中的 Azure、Chef 中的 Azure-Storage、Ruby 中的 Azure-Storage)。
我发现的针对此类问题的大多数故障排除都建议流式传输或分块下载,但似乎没有相应的方法或 get_blob 选项来执行此操作。
代码:
require 'azure/storage'
# vars
account_name = "myacct"
container_name = "myfiles"
access_key = "mykey"
installs_dir = "myinstalls"
# directory for files
create_dir = 'c:/' + installs_dir
Dir.mkdir(create_dir) unless File.exists?(create_dir)
# create azure client
Azure::Storage.setup(:storage_account_name => account_name, :storage_access_key => access_key)
azBlobs = Azure::Storage::Blob::BlobService.new
# get list of blobs in container
dlBlobs = azBlobs.list_blobs(container_name)
# download each blob to directory
dlBlobs.each do |dlBlob|
puts "Downloading " + container_name + "/" + dlBlob.name
portalBlob, blobContent = azBlobs.get_blob(container_name, dlBlob.name)
File.open("c:/" + installs_dir + "/" + portalBlob.name, "wb") {|f|
f.write(blobContent)
}
end
我也尝试使用 IO.binwrite() 而不是 File.open() 并得到相同的结果。
建议?
问题是 get_blob
必须立即将数据加载到内存中,而不是将其流式传输到磁盘。在 Chef 中,我们有 remote_file
资源来帮助进行此流式下载,但您需要为 blob 获取普通的 URL,而不是使用他们的 gem.
下载它
正如@coderanger 所说,您的问题是由于使用 get_blob
将本地数据立即存入内存引起的。有两种解决方法。
- 根据官方 REST 参考 here 如下。
The maximum size for a block blob created via Put Blob is 256 MB for version 2016-05-31 and later, and 64 MB for older versions. If your blob is larger than 256 MB for version 2016-05-31 and later, or 64 MB for older versions, you must upload it as a set of blocks. For more information, see the Put Block and Put Block Listoperations. It's not necessary to also call Put Blob if you upload the blob as a set of blocks.
所以对于由块blob组成的blob,您可以尝试通过list_blob_blocks
获取块blob列表,将这些块blob一个一个地写入本地文件。
- 像this test code一样通过
signed_uri
生成带有SAS令牌的blob url,然后通过流下载blob以写入本地文件。
我只是在考虑将 azure/storage/blob
库用于我正在从事的开发运营项目,在我看来,该实现非常基础,没有利用完整的底层 API 可用的。例如,从文件流式传输时上传速度很慢,因为很可能它不是并行上传块等。我不认为这个库已准备好生产并且缺少公开的 ruby API 。它是开源的,所以如果有人有时间,他们可以帮助做出贡献。
环境:
- Windows 10 x64
- Ruby 2.1.0 32 位
- 大厨 12.12.15
- 蔚蓝 Gem 0.7.9
- Azure 存储 Gem 0.12.1.preview
我正在尝试从容器中下载约 880MB 的 blob。当我这样做时,它会在 Ruby 进程大小达到 ~500MB 后抛出以下错误:
C:/opscode/chefdk/embedded/lib/ruby/2.1.0/net/protocol.rb:102:in `read': failed to allocate memory (NoMemoryError)
我已经在 Ruby 内部和外部以及 Azure gem 和 Azure-Storage gem 上进行了尝试。结果与所有四种组合相同(Chef 中的 Azure、Ruby 中的 Azure、Chef 中的 Azure-Storage、Ruby 中的 Azure-Storage)。
我发现的针对此类问题的大多数故障排除都建议流式传输或分块下载,但似乎没有相应的方法或 get_blob 选项来执行此操作。
代码:
require 'azure/storage'
# vars
account_name = "myacct"
container_name = "myfiles"
access_key = "mykey"
installs_dir = "myinstalls"
# directory for files
create_dir = 'c:/' + installs_dir
Dir.mkdir(create_dir) unless File.exists?(create_dir)
# create azure client
Azure::Storage.setup(:storage_account_name => account_name, :storage_access_key => access_key)
azBlobs = Azure::Storage::Blob::BlobService.new
# get list of blobs in container
dlBlobs = azBlobs.list_blobs(container_name)
# download each blob to directory
dlBlobs.each do |dlBlob|
puts "Downloading " + container_name + "/" + dlBlob.name
portalBlob, blobContent = azBlobs.get_blob(container_name, dlBlob.name)
File.open("c:/" + installs_dir + "/" + portalBlob.name, "wb") {|f|
f.write(blobContent)
}
end
我也尝试使用 IO.binwrite() 而不是 File.open() 并得到相同的结果。
建议?
问题是 get_blob
必须立即将数据加载到内存中,而不是将其流式传输到磁盘。在 Chef 中,我们有 remote_file
资源来帮助进行此流式下载,但您需要为 blob 获取普通的 URL,而不是使用他们的 gem.
正如@coderanger 所说,您的问题是由于使用 get_blob
将本地数据立即存入内存引起的。有两种解决方法。
- 根据官方 REST 参考 here 如下。
The maximum size for a block blob created via Put Blob is 256 MB for version 2016-05-31 and later, and 64 MB for older versions. If your blob is larger than 256 MB for version 2016-05-31 and later, or 64 MB for older versions, you must upload it as a set of blocks. For more information, see the Put Block and Put Block Listoperations. It's not necessary to also call Put Blob if you upload the blob as a set of blocks.
所以对于由块blob组成的blob,您可以尝试通过list_blob_blocks
获取块blob列表,将这些块blob一个一个地写入本地文件。
- 像this test code一样通过
signed_uri
生成带有SAS令牌的blob url,然后通过流下载blob以写入本地文件。
我只是在考虑将 azure/storage/blob
库用于我正在从事的开发运营项目,在我看来,该实现非常基础,没有利用完整的底层 API 可用的。例如,从文件流式传输时上传速度很慢,因为很可能它不是并行上传块等。我不认为这个库已准备好生产并且缺少公开的 ruby API 。它是开源的,所以如果有人有时间,他们可以帮助做出贡献。