8

有谁知道在 rails 5.2+ 上使用 ActiveStorage 时如何计算 active_storage_blobs 中的校验和字段?

对于奖励积分,有谁知道我怎样才能使用与 md5 CLI 命令中的校验和匹配的 md5 校验和?

4

4 回答 4

8

让我们分解一下

我知道我参加聚会有点晚了,但这对于那些在寻找答案时遇到这个问题的人来说更是如此。所以在这里..

背景:

Rails 在 5.2 版本中引入了大量新特性,其中之一就是ActiveStorage。官方最终版本于 2018 年 4 月 9 日发布。

免责声明:

因此,为了清楚起见,以下信息与开箱即用的香草主动存储有关。这也没有考虑到一些围绕某个场景的疯狂代码fu。

话虽如此,校验和的计算方式会根据您的 Active Storage 设置而有所不同。使用 vanilla 开箱即用的 Rails Active Storage,有 2 种“类型”(因为没有更好的术语)配置。

  1. 代理上传
  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 源代码的片段 - (activestorage.js)

  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

于 2019-03-28T18:46:53.740 回答
6

源代码在这里: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>
于 2018-11-27T07:45:50.233 回答
2

它是 blob 数据的 base64 编码的 MD5 摘要。恐怕 Active Storage 不支持md5(1)发出的十六进制校验和。对不起!

于 2018-06-02T14:32:56.270 回答
2

对于您的奖金问题(可能也是主要问题):

您可以将校验和从 base64 转换为十六进制(如md5(1)命令支持)并返回。

在 Ruby 中将十六进制摘要转换为 base64

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
于 2020-10-26T13:37:51.820 回答