我们在 Heroku 托管的 Rails 应用程序上的后台工作程序上生成 PDF 文件,一旦生成,它们就会上传到 Amazon S3。 Heroku 应用程序和 S3 存储桶均位于 eu-west-1 区域。
我们的上传速度非常慢,尽管文件非常基本且很小。看这个例子:
Aws.config.update({
region: 'eu-west-1',
credentials: Aws::Credentials.new(ENV['S3_USER_KEY'], ENV['S3_USER_SECRET'])
})
S3_BUCKET = Aws::S3::Resource.new.bucket(ENV['S3_PRIVATE_BUCKET'])
file = Tempfile.new(["testfile", ".pdf"], encoding: "ascii-8bit").tap do |file|
file.write("a"*5000)
end
Benchmark.bm do |x|
x.report { S3_BUCKET.put_object(key: "testfile.pdf", body: file) }
end
user system total real
0.020000 0.040000 0.060000 ( 40.499553)
我想我无法举一个更简单的例子,因此发送一个包含 5000 个字符的文件需要 40 秒才能从 Heroku 一次性实例上传到 S3。
请注意,我在我的(国内)互联网连接和 Heroku 实例上进行了测试,结果几乎相似。 另一方面,我使用 ForkLift.app 作为 GUI 来浏览我的存储桶,并且上传文件几乎是瞬时的。
我一直在浏览 aws-sdk 文档,但看不到任何内容来解释如此缓慢的上传
看来是put_object和TempFile的问题
先尝试将文件传递给IO
new_file = IO.read(file)
S3_BUCKET.put_object(key: "testfile.pdf", body: new_file)
看起来 AwsSdk 是罪魁祸首。 我测试了其他方式上传相同的文件:
(我使用手机连接,因此网络速度非常慢,而且我没有花时间在 Heroku Dyno 上安装/配置 aws CLI)
Benchmark.bm do |x|
x.report { `aws s3 cp #{file.path} s3://#{ ENV['S3_BUCKET']}/testfile.pdf` }
end
0.000000 0.000000 0.510000 ( 2.486112)
这是从 Heroku Dyno 运行的。
connection = Fog::Storage.new({
:provider => 'AWS',
:aws_access_key_id => ENV['S3_USER_KEY'],
:aws_secret_access_key => ENV['S3_USER_SECRET'],
region: "eu-west-1"
})
directory = connection.directories.new(key: ENV["S3_BUCKET"], region: "eu-west-1")
Benchmark.bmb do |x|
x.report do
directory.files.create(
:key => 'test-with-fog.pdf',
:body => file,
)
end
end
user system total real
0.010000 0.010000 0.020000 ( 0.050712)
我将坚持使用最新版本作为解决方法。不过,我没有找到导致 aws-sdk 如此缓慢的原因。
我在创建
Aws::S3::Object
并使用方法 upload_file
时遇到了类似的问题。如果我传入一个 TempFile
对象,上传一个小文件(~5KB)也需要~40 秒。然而,传入TempFile.path
的速度快得惊人(不到1秒)。
出于自己的原因,您可能需要使用
AWS::S3::Bucket
方法 put_object
,但是 put_object
似乎只接受 String
或 IO
,而不是 File
或 TempFile
路径。如果您可以重构以创建 AWS::S3::Object
并使用 upload_file
,您可以使用此解决方法。
require 'aws-sdk-s3'
s3_resource = Aws::S3::Resource.new(region: 'us-east-2')
file = Tempfile.new(["testfile", ".pdf"], encoding: "ascii-8bit").tap do |file|
file.write("a"*5000)
end
Benchmark.bm do |x|
x.report {
obj = s3_resource.bucket('mybucket').object("testfile-as-object.pdf")
#passing the TempFile object is quite slow
obj.upload_file(file)
}
end
# user system total real
# 0.010359 0.006335 0.016694 ( 41.175544)
Benchmark.bm do |x|
x.report {
obj = s3_resource.bucket('mybucket').object("testfile-as-path.pdf")
#passing the TempFile object's path is massively faster than passing the TempFile object itself
obj.upload_file(file.path)
}
end
# user system total real
# 0.004573 0.002032 0.006605 ( 0.398605)
我认为你在 put_object 中缺少 content_type。
S3_BUCKET.put_object(key: "testfile.pdf", body: 文件, content_type: "application/pdf")
我与此具有相同的测试性能,对于 pdf 文件上传,它会从 20 秒增加到 2 毫秒。
希望这有帮助。
我的解决方案:
Benchmark.bm do |x|
file.seek(0) # add this
x.report { S3_BUCKET.put_object(key: "testfile.pdf", body: file) }
end
# user system total real
# 0.003022 0.000740 0.003762 (0.018425)