跳到本文 跳到搜尋

Active Storage Blob

Blob 是記錄,其中包含關於檔案的附屬資料和該檔案在服務上位置的鍵值。Blob 可透過以下兩種方式建立

  1. 在檔案經由伺服器端上傳至服務之前,使用 create_and_upload!。此作業必須在伺服器上使用可回溯的 io 和檔案內容。

  2. 在檔案直接上傳至服務的客戶端之前,使用 create_before_direct_upload!

第一個選項不需要任何客戶端 JavaScript 整合,並且可以由處理檔案的任何其它後端服務使用。第二個選項較快,因為您不用把自己的伺服器當成上傳的暫存點,並且可以與不提供大量磁碟空間的部署(例如 Heroku)搭配使用。

Blob 旨於對專門檔案參考保持不變。允許您在後續的傳遞中更新 blob 的附屬資料,但您不應該更新鍵值或變更上傳的檔案。如果您需要建構衍生品,或是變更 blob,只需建構新的 blob,並移除舊的 blob。

命名空間
方法
A
C
D
F
G
I
K
O
P
S
T
U
V
包含的模組

常數

MINIMUM_TOKEN_LENGTH = 28
 

類別共用方法

compose(blobs, key: nil, filename:, content_type: nil, metadata: nil)

將多個 blob 合併成單一的「已組合」blob。

# File activestorage/app/models/active_storage/blob.rb, line 144
def compose(blobs, key: nil, filename:, content_type: nil, metadata: nil)
  raise ActiveRecord::RecordNotSaved, "All blobs must be persisted." if blobs.any?(&:new_record?)

  content_type ||= blobs.pluck(:content_type).compact.first

  new(key: key, filename: filename, content_type: content_type, metadata: metadata, byte_size: blobs.sum(&:byte_size)).tap do |combined_blob|
    combined_blob.compose(blobs.pluck(:key))
    combined_blob.save!
  end
end

create_and_upload!(key: nil, io:, filename:, content_type: nil, metadata: nil, service_name: nil, identify: true, record: nil)

建立新的 blob 實例,然後上傳給定 io 的內容到服務。blob 實例會在開始上傳之前儲存,以防止上傳因為金鑰衝突而覆寫掉其他 blob。當提供內容類型時,傳遞 identify: false 以略過自動內容類型推論。

# File activestorage/app/models/active_storage/blob.rb, line 95
def create_and_upload!(key: nil, io:, filename:, content_type: nil, metadata: nil, service_name: nil, identify: true, record: nil)
  create_after_unfurling!(key: key, io: io, filename: filename, content_type: content_type, metadata: metadata, service_name: service_name, identify: identify).tap do |blob|
    blob.upload_without_unfurling(io)
  end
end

create_before_direct_upload!(key: nil, filename:, byte_size:, checksum:, content_type: nil, metadata: nil, service_name: nil, record: nil)

傳回已儲存的 blob,而不將檔案上傳到服務。此 blob 將指向尚未有檔案的金鑰。預期搭配客戶端上傳一起使用,這會先建立 blob 以產生用於上傳的簽署網址。此簽署網址指向blob產生的金鑰。一旦提交使用直接上傳的表單,就可以使用簽署 ID 將blob關聯到正確的記錄。

# File activestorage/app/models/active_storage/blob.rb, line 106
def create_before_direct_upload!(key: nil, filename:, byte_size:, checksum:, content_type: nil, metadata: nil, service_name: nil, record: nil)
  create! key: key, filename: filename, byte_size: byte_size, checksum: checksum, content_type: content_type, metadata: metadata, service_name: service_name
end

find_signed(id, record: nil, purpose: :blob_id)

可以使用 blob 的簽署 ID 在客戶端端參考它,而不用擔心被竄改。對於直接上傳特別有幫助,其中客户端需要在表單提交時參考上傳之前建立的 blob。

簽署 ID 還有透過 BlobsController 為 blob 建立穩定網址。

# File activestorage/app/models/active_storage/blob.rb, line 69
def find_signed(id, record: nil, purpose: :blob_id)
  super(id, purpose: purpose)
end

find_signed!(id, record: nil, purpose: :blob_id)

運作方式與 `find_signed` 相同,但是如果 `signed_id` 已過期、目的不符或已被竄改,會引發 `ActiveSupport::MessageVerifier::InvalidSignature` 例外。如果有效簽署 ID 無法找到記錄,它還會引發 `ActiveRecord::RecordNotFound` 例外。

# File activestorage/app/models/active_storage/blob.rb, line 76
def find_signed!(id, record: nil, purpose: :blob_id)
  super(id, purpose: purpose)
end

generate_unique_secure_token(length: MINIMUM_TOKEN_LENGTH)

為了防止大小寫不敏感的檔案系統出問題,特別是搭配將索引視為大小寫敏感的資料庫時,所有產生的 blob 金鑰只會包含 base-36 字元,因此會是全部小寫。為維持與 `has_secure_token` 使用的 base-58 編碼相同或更高的熵值,所使用的位元組數從標準的 24 個增加到 28 個

# File activestorage/app/models/active_storage/blob.rb, line 115
def generate_unique_secure_token(length: MINIMUM_TOKEN_LENGTH)
  SecureRandom.base36(length)
end

未附檔

傳回未附檔到任一記錄的 blob。

# File activestorage/app/models/active_storage/blob.rb, line 38
scope :unattached, -> { where.missing(:attachments) }

執行個體公用方法

附檔

傳回關聯的 ActiveStorage::Attachment 個體。

# File activestorage/app/models/active_storage/blob.rb, line 32
has_many :attachments

音訊?()

如果這個 blob 的內容類型屬於音訊範圍,例如 audio/mpeg,則傳回 true。

# File activestorage/app/models/active_storage/blob.rb, line 212
def audio?
  content_type.start_with?("audio")
end

自訂元資料()

# File activestorage/app/models/active_storage/blob.rb, line 198
def custom_metadata
  self[:metadata][:custom] || {}
end

自訂元資料=(metadata)

# File activestorage/app/models/active_storage/blob.rb, line 202
def custom_metadata=(metadata)
  self[:metadata] = self[:metadata].merge(custom: metadata)
end

刪除()

刪除與 blob 關聯的服務上的檔案。這只應在 blob 準備要刪除,否則你將會擁有實際上已死的參考。建議在多數情況下,使用 purgepurge_later 方法。

# File activestorage/app/models/active_storage/blob.rb, line 322
def delete
  service.delete(key)
  service.delete_prefixed("variants/#{key}/") if image?
end

下載(&block)

下載與這個 blob 關聯的檔案。如果沒有給出區塊,則會將整個檔案讀取到記憶體中並傳回。對於超大型檔案,這樣會使用大量的 RAM。如果給出區塊,則會串流下載並以區塊為單位讓出。

# File activestorage/app/models/active_storage/blob.rb, line 282
def download(&block)
  service.download key, &block
end

下載區塊(range)

下載與這個 blob 關聯的檔案的部分內容。

# File activestorage/app/models/active_storage/blob.rb, line 287
def download_chunk(range)
  service.download_chunk key, range
end

檔案名稱()

傳回檔案名稱的 ActiveStorage::Filename 實例,可查詢基本檔名、副檔名和安全版本(可安全使用於 URL)的檔名。

# File activestorage/app/models/active_storage/blob.rb, line 194
def filename
  ActiveStorage::Filename.new(self[:filename])
end

image?()

如果這項 blob 的 content_type 屬於影像範圍(例如 image/png),則傳回 true。

# File activestorage/app/models/active_storage/blob.rb, line 207
def image?
  content_type.start_with?("image")
end

key()

傳回指向與此 blob 關聯的服務上的檔案的 key。此 key 是小寫的 Rails 安全令牌格式。因此看起來像:xtapjjcjiudrlk3tmwyjgpuobabd。這項 key 並非預計直接顯示給使用者。請務必使用 signed_id 或驗證過的 key 形式來參考 blob。

# File activestorage/app/models/active_storage/blob.rb, line 186
def key
  # We can't wait until the record is first saved to have a key for it
  self[:key] ||= self.class.generate_unique_secure_token(length: MINIMUM_TOKEN_LENGTH)
end

open(tmpdir: nil, &block)

下載 blob 到磁碟上的暫存檔案。傳回暫存檔案。

暫存檔案的名稱加上 ActiveStorage- 前綴和 blob 的 ID。其副檔名與 blob 的副檔名相符。

預設在 Dir.tmpdir 中建立暫存檔案。傳遞 tmpdir: 在不同的目錄中建立

blob.open(tmpdir: "/path/to/tmp") do |file|
  # ...
end

執行所提供的區塊後,暫存檔案會自動關閉和解除連結。

如果下載的資料與 blob 的檢查碼不符,會引发 ActiveStorage::IntegrityError

# File activestorage/app/models/active_storage/blob.rb, line 304
def open(tmpdir: nil, &block)
  service.open(
    key,
    checksum: checksum,
    verify: !composed,
    name: [ "ActiveStorage-#{id}-", filename.extension_with_delimiter ],
    tmpdir: tmpdir,
    &block
  )
end

purge()

清除 blob 記錄,然後刪除服務中的檔案。這是處理不需要的 blob 的建議方法。但請注意,從服務中刪除檔案會初始化與服務的 HTTP 連線,可能會很慢或無法進行,因此您不可以在交易或 CallBack 中使用此方法。請改用 purge_later

# File activestorage/app/models/active_storage/blob.rb, line 330
def purge
  destroy
  delete if previously_persisted?
rescue ActiveRecord::InvalidForeignKey
end

purge_later()

排定 ActiveStorage::PurgeJob 來呼叫 purge。這是從交易、ActiveRecord CallBack 或任何其他即時場景中清除 blob 的建議方法。

# File activestorage/app/models/active_storage/blob.rb, line 338
def purge_later
  ActiveStorage::PurgeJob.perform_later(self)
end

service()

傳回服務的實例,可以針對全局或每個附加元件設定

# File activestorage/app/models/active_storage/blob.rb, line 343
def service
  services.fetch(service_name)
end

service_headers_for_direct_upload()

傳回一個 Hash,其中包含 `service_url_for_direct_upload` 要求的標頭。

# File activestorage/app/models/active_storage/blob.rb, line 242
def service_headers_for_direct_upload
  service.headers_for_direct_upload key, filename: filename, content_type: content_type, content_length: byte_size, checksum: checksum, custom_metadata: custom_metadata
end

service_url_for_direct_upload(expires_in: ActiveStorage.service_urls_expire_in)

傳回此服務上可直接上傳 blob 檔案的 URL。此 URL 僅供暫時使用以維持安全性,且只會在負責上傳的用戶端 JavaScript 請求時才產生。

# File activestorage/app/models/active_storage/blob.rb, line 237
def service_url_for_direct_upload(expires_in: ActiveStorage.service_urls_expire_in)
  service.url_for_direct_upload key, expires_in: expires_in, content_type: content_type, content_length: byte_size, checksum: checksum, custom_metadata: custom_metadata
end

signed_id(purpose: :blob_id, expires_in: nil, expires_at: nil)

傳回此 blob 的簽署 ID,此 ID 適用於用戶端,無需擔心遭到入侵。

# File activestorage/app/models/active_storage/blob.rb, line 178
def signed_id(purpose: :blob_id, expires_in: nil, expires_at: nil)
  super
end

text?()

如果此 blob 的 content_type 屬於文字類別(例如文字/普通),則傳回 true。

# File activestorage/app/models/active_storage/blob.rb, line 222
def text?
  content_type.start_with?("text")
end

upload(io, identify: true)

io 上傳至此 blob 的 key 上的服務。Blob 應為不可變動,因此在檔案已上傳至符合 blob 後,請勿使用此方法。如果您想要建立派生 blob,您應該直接根據舊 blob 建立新的 blob。

上傳前,我們會計算雜湊,並將其傳送至服務以進行傳輸完整性驗證。如果雜湊與服務接收到的資料不符,則會引發例外狀況。我們也會測量 io 的大小,並將其儲存在 blob 記錄的 byte_size 中。內容類型會自動從 io 中提取,除非您指定一個 content_type 並將 identify 傳遞為 false。

正常情況下,您根本不必直接呼叫此方法。請改用 create_and_upload! 類別方法。如果您直接使用此方法,請確定您在持久的 Blob 上使用它,否則可能會覆寫服務上其他 blob 的資料。

# File activestorage/app/models/active_storage/blob.rb, line 259
def upload(io, identify: true)
  unfurl io, identify: identify
  upload_without_unfurling io
end

url(expires_in: ActiveStorage.service_urls_expire_in, disposition: :inline, filename: nil, **options)

傳回服務上 blob 的 URL。對於公開的檔案,此處會傳回永久 URL,而對於私有的檔案,此處會傳回暫時 URL。私人檔案是簽署的,不可公開使用。相反的,URL 應該只以重定向的方式,從穩定且可能已驗證的 URL 顯示。將 URL 隱藏在重定向後面,還能讓您在不更新所有 URL 的情況下變更服務。

# File activestorage/app/models/active_storage/blob.rb, line 230
def url(expires_in: ActiveStorage.service_urls_expire_in, disposition: :inline, filename: nil, **options)
  service.url key, expires_in: expires_in, filename: ActiveStorage::Filename.wrap(filename || self.filename),
    content_type: content_type_for_serving, disposition: forced_disposition_for_serving || disposition, **options
end

video?()

如果此 blob 的 content_type 位於視訊範圍中,如 video/mp4,則傳回 true。

# File activestorage/app/models/active_storage/blob.rb, line 217
def video?
  content_type.start_with?("video")
end