【发布时间】:2018-11-12 10:24:25
【问题描述】:
有人知道在 Rails 5.2+ 上使用 ActiveStorage 时如何计算 active_storage_blobs 中的校验和字段吗?
对于奖励积分,有谁知道我如何让它使用与 md5 CLI 命令中的校验和匹配的 md5 校验和?
【问题讨论】:
标签: ruby-on-rails rails-activestorage
有人知道在 Rails 5.2+ 上使用 ActiveStorage 时如何计算 active_storage_blobs 中的校验和字段吗?
对于奖励积分,有谁知道我如何让它使用与 md5 CLI 命令中的校验和匹配的 md5 校验和?
【问题讨论】:
标签: ruby-on-rails rails-activestorage
这是 blob 数据的 base64 编码 MD5 摘要。恐怕 Active Storage 不支持像 md5(1) 发出的十六进制校验和。对不起!
【讨论】:
源代码在这里:https://github.com/rails/rails/blob/master/activestorage/app/models/active_storage/blob.rb#L234
def compute_checksum_in_chunks(io)
Digest::MD5.new.tap do |checksum|
while chunk = io.read(5.megabytes)
checksum << chunk
end
io.rewind
end.base64digest
end
在我的项目中,我需要使用这个校验和值来判断用户是否上传了重复文件,我使用下面的代码与上面的方法得到相同的值:
md5 = Digest::MD5.file(params[:file].tempfile.path).base64digest
puts "========= md5: #{md5}"
输出:
========= md5: F/9Inmc4zdQqpeSS2ZZGug==
数据库数据:
pry(main)> ActiveStorage::Blob.find_by(checksum: 'F/9Inmc4zdQqpeSS2ZZGug==')
ActiveStorage::Blob Load (2.7ms) SELECT "active_storage_blobs".* FROM "active_storage_blobs" WHERE "active_storage_blobs"."checksum" = $1 LIMIT $2 [["checksum", "F/9Inmc4zdQqpeSS2ZZGug=="], ["LIMIT", 1]]
=> #<ActiveStorage::Blob:0x00007f9a16729a90
id: 1,
key: "gpN2NSgfimVP8VwzHwQXs1cB",
filename: "15 Celebrate.mp3",
content_type: "audio/mpeg",
metadata: {"identified"=>true, "analyzed"=>true},
byte_size: 9204528,
checksum: "F/9Inmc4zdQqpeSS2ZZGug==",
created_at: Thu, 29 Nov 2018 01:38:15 UTC +00:00>
【讨论】:
我知道我参加聚会有点晚了,但这更适合那些在寻找答案时遇到这个问题的人。所以这里是..
背景:
Rails 在 5.2 版中引入了大量新功能,其中之一是 ActiveStorage。官方最终版本于 2018 年 4 月 9 日发布。
免责声明:
为了清楚起见,以下信息与开箱即用的 vanilla 主动存储有关。这也没有考虑到一些围绕某个场景的疯狂代码fu。
话虽如此,校验和的计算方式因您的 Active Storage 设置而异。使用普通的开箱即用 Rails Active Storage,有 2 种“类型”(没有更好的术语)配置。
文件上传流程: [Client] → [RoR App] → [Storage Service]
通讯。流程:可能会有所不同,但在大多数情况下,它应该类似于文件上传流程。
上面 SparkBao 的回答中指出的是“代理上传”。这意味着您将文件上传到您的 RoR 应用程序并执行某种处理,然后再将文件发送到您配置的存储服务(AWS、Azure、Google、BackBlaze 等)。即使您将存储服务设置为“localdisk”,该逻辑在技术上仍然适用,即使您的 RoR 应用程序是存储端点。
“代理上传”方法并不适合在 Heroku 等服务上部署在云中的 RoR 应用程序。 Heroku 有 30 秒的硬性限制来完成您的交易并将响应发送回您的客户(最终用户)。因此,如果您的文件相当大,则需要考虑文件上传所需的时间,然后考虑计算校验和所需的时间。如果您遇到无法在 30 秒内完成请求并做出响应的情况,您将需要使用“直接上传”方法。
代理上传答案:
正如 Spark.Bao 所指出的,Ruby 类 Digest::MD5 用于方法 compute_checksum_in_chunks(io)。
文件上传流程: [客户端] → [存储服务]
通讯。流程: [Client] → [RoR App] → [Client] → [Storage Service] → [Client] → [RoR App] → [Client]
维护和开发 Rails 的好朋友已经为我们完成了所有繁重的工作。我不会详细介绍如何设置直接上传,但这里有一个关于如何设置的链接 »Rails EdgeGuide - Direct Uploads。
代理上传答案:
现在说了这么多,通过开箱即用的“直接上传”设置,文件校验和是通过利用 SparkMD5 (JavaScript) 来计算的。
下面是来自Rails Active Storage Source Code- (activestorage.js)的sn-p
var fileSlice = File.prototype.slice || File.prototype.mozSlice || File.prototype.webkitSlice;
var FileChecksum = function() {
createClass(FileChecksum, null, [ {
key: "create",
value: function create(file, callback) {
var instance = new FileChecksum(file);
instance.create(callback);
}
} ]);
function FileChecksum(file) {
classCallCheck(this, FileChecksum);
this.file = file;
this.chunkSize = 2097152;
this.chunkCount = Math.ceil(this.file.size / this.chunkSize);
this.chunkIndex = 0;
}
createClass(FileChecksum, [ {
key: "create",
value: function create(callback) {
var _this = this;
this.callback = callback;
this.md5Buffer = new sparkMd5.ArrayBuffer();
this.fileReader = new FileReader();
this.fileReader.addEventListener("load", function(event) {
return _this.fileReaderDidLoad(event);
});
this.fileReader.addEventListener("error", function(event) {
return _this.fileReaderDidError(event);
});
this.readNextChunk();
}
},
如果有什么我错过的,我会提前道歉。我尽量做到彻底。
所以总结一下以下应该足以作为可接受的答案:
代理上传配置: ruby 类Digest::MD5
直接上传配置: JavaScript 哈希库SparkMD5。
【讨论】:
对于您的额外问题(可能也是主要问题):
您可以将校验和从 base64 转换为十六进制(如 md5(1) 命令支持)并返回。
Converting a hexadecimal digest to base64 in Ruby:
def hex_to_base64(hexdigest)
Base64.strict_encode64([hex_string].pack("H*"))
end
从base64到十六进制:
def base64_to_hex(base64_string)
Base64.decode64(base64_string).each_byte.map { |b| "%02x" % b.to_i }.join
end
【讨论】: