将 CSV 流从 Ruby 上传到 S3
Posted
技术标签:
【中文标题】将 CSV 流从 Ruby 上传到 S3【英文标题】:Upload CSV stream from Ruby to S3 【发布时间】:2016-05-22 19:53:01 【问题描述】:我正在处理我想从我的 Rails 应用程序导出的潜在巨大 CSV 文件,并且由于它在 Heroku 上运行,我的想法是在生成这些 CSV 文件时将它们直接流式传输到 S3。
现在,我有一个问题,Aws::S3
需要一个文件才能执行上传,而在我的 Rails 应用程序中,我想做类似的事情:
S3.bucket('my-bucket').object('my-csv') << %w(this is one line)
我怎样才能做到这一点?
【问题讨论】:
您看过 S3 Multipart gem 吗? github.com/maxgillett/s3_multipart 我认为在没有生成之前直接将文件保存到 s3 是可能的。 我正在动态构建 CSV,在后台作业中,我不通过某种界面上传它。 @AndreiHorak 你找到解决方案了吗? @Tonja 不幸的是,我没有时间深入研究它。希望你会遇到更好的运气! @linkyndy 我也有同样的要求。您对此有任何解决方案吗? 【参考方案1】:您可以使用 s3 分段上传,它允许通过将大对象拆分为多个块进行上传。 https://docs.aws.amazon.com/AmazonS3/latest/dev/mpuoverview.html
分段上传需要更复杂的编码,但 aws-sdk-ruby V3 支持upload_stream
方法,该方法似乎在内部执行分段上传,并且非常易于使用。也许这个用例的确切解决方案。
https://docs.aws.amazon.com/sdk-for-ruby/v3/api/Aws/S3/Object.html#upload_stream-instance_method
client = Aws::S3::Client.new(
region: 'ap-northeast-1',
credentials: your_credential
)
obj = Aws::S3::Object.new('your-bucket-here', 'path-to-output', client: client)
require "csv"
obj.upload_stream do |write_stream|
[
%w(this is first line),
%w(this is second line),
%w(this is third line),
].each do |line|
write_stream << line.to_csv
end
end
this,is,first,line
this,is,second,line
this,is,third,line
upload_stream
块的参数通常可以用作 IO 对象,它允许您像处理文件或其他 IO 对象一样链接和包装 CSV 生成:
obj.upload_stream do |write_stream|
CSV(write_stream) do |csv|
[
%w(this is first line),
%w(this is second line),
%w(this is third line),
].each do |line|
csv << line
end
end
end
或者,例如,您可以在生成和上传 CSV 时对其进行压缩,使用临时文件来减少内存占用:
obj.upload_stream(tempfile: true) do |write_stream|
# When uploading compressed data, use binmode to avoid an encoding error.
write_stream.binmode
Zlib::GzipWriter.wrap(write_stream) do |gzw|
CSV(gzw) do |csv|
[
%w(this is first line),
%w(this is second line),
%w(this is third line),
].each do |line|
csv << line
end
end
end
end
已编辑:在压缩的示例代码中,您必须添加binmode
以修复以下错误:
Aws::S3::MultipartUploadError: multipart upload failed: "\x8D" from ASCII-8BIT to UTF-8
【讨论】:
谢谢三俊!这正是我一直在寻找的。我还添加了基于块的 CSV 生成器和 IO 包装器的示例,它们也可用于在上传到 S3 之前通过 gzip 进行压缩 你知道我用upload_stream上传文件时如何设置内容类型吗? @Kazuki 你可以像这样传递参数。s3obj.upload_stream(acl: 'private', content_type: 'text/csv; charset=UTF-8') do |stream| ...
s3obj 应该是这样的 s3obj = Aws::S3::Object.new('your-bucket-name', path, client: client)
【参考方案2】:
s3 = Aws::S3::Resource.new(region:'us-west-2')
obj = s3.bucket.object("#FOLDER_NAME/#file_name.csv")
file_csv = CSV.generate do |csv|
csv << ActionLog.column_names
ActionLog.all.each do |action_log|
csv << action_log.attributes.values
end
end
obj.put body: file_csv
file_csv = CSV.generate
是在 Ruby 中创建一串 CSV 数据。创建这个 CSV 字符串后,我们使用存储桶将路径放入 S3
#FOLDER_NAME/#file_name.csv
在我的代码中,我将所有数据导出到 ActionLog
模型。
【讨论】:
用解释包围你的答案会大大改善它。 谢谢!我已经更新了我的答案,但是我的英语不好,对不起:( 我非常努力地改进您的语法和拼写。但我承认我的方法可能不够。我无法推断出所有的含义。 这不能回答我的问题。我特别提到“流”。检查我对另一个答案的回复。 请注意ActionLog.all.each
会将所有 ActionLog
s 加载到 RAM 中。你应该这样做ActionLog.find_each
。【参考方案3】:
我会看看http://docs.aws.amazon.com/AWSRubySDK/latest/AWS/S3/S3Object.html#write-instance_method,因为这可能就是您要找的。p>
编辑 http://docs.aws.amazon.com/AmazonS3/latest/dev/UploadObjSingleOpRuby.html 可能更相关,因为第一个链接指向 ruby aws-sdk v1
require 'aws-sdk'
s3 = Aws::S3::Resource.new(region:'us-west-2')
obj = s3.bucket('bucket-name').object('key')
# string data
obj.put(body: 'Hello World!')
# IO object
File.open('source', 'rb') do |file|
obj.put(body: file)
end
【讨论】:
这仍然是一个已经构建的文件。我想逐步上传文件的块。以上是关于将 CSV 流从 Ruby 上传到 S3的主要内容,如果未能解决你的问题,请参考以下文章
如何将抓取的数据从 Scrapy 以 csv 或 json 格式上传到 Amazon S3?