diff options
Diffstat (limited to 'activestorage/app/models')
-rw-r--r-- | activestorage/app/models/active_storage/blob.rb | 2 | ||||
-rw-r--r-- | activestorage/app/models/active_storage/filename.rb | 24 | ||||
-rw-r--r-- | activestorage/app/models/active_storage/filename/parameters.rb | 36 |
3 files changed, 52 insertions, 10 deletions
diff --git a/activestorage/app/models/active_storage/blob.rb b/activestorage/app/models/active_storage/blob.rb index 9f2ed1e5ac..664a53a778 100644 --- a/activestorage/app/models/active_storage/blob.rb +++ b/activestorage/app/models/active_storage/blob.rb @@ -127,7 +127,7 @@ class ActiveStorage::Blob < ActiveRecord::Base # Hiding the +service_url+ behind a redirect also gives you the power to change services without updating all URLs. And # it allows permanent URLs that redirect to the +service_url+ to be cached in the view. def service_url(expires_in: 5.minutes, disposition: :inline) - service.url key, expires_in: expires_in, disposition: disposition, filename: filename, content_type: content_type + service.url key, expires_in: expires_in, disposition: "#{disposition}; #{filename.parameters}", filename: filename, content_type: content_type end # Returns a URL that can be used to directly upload a file for this blob on the service. This URL is intended to be diff --git a/activestorage/app/models/active_storage/filename.rb b/activestorage/app/models/active_storage/filename.rb index 6a9889addf..c2ad5c844c 100644 --- a/activestorage/app/models/active_storage/filename.rb +++ b/activestorage/app/models/active_storage/filename.rb @@ -9,21 +9,23 @@ class ActiveStorage::Filename @filename = filename end - # Filename.new("racecar.jpg").extname # => ".jpg" - def extname - File.extname(@filename) + # Filename.new("racecar.jpg").base # => "racecar" + def base + File.basename @filename, extension_with_delimiter end - # Filename.new("racecar.jpg").extension # => "jpg" - def extension - extname.from(1) + # Filename.new("racecar.jpg").extension_with_delimiter # => ".jpg" + def extension_with_delimiter + File.extname @filename end - # Filename.new("racecar.jpg").base # => "racecar" - def base - File.basename(@filename, extname) + # Filename.new("racecar.jpg").extension_without_delimiter # => "jpg" + def extension_without_delimiter + extension_with_delimiter.from(1).to_s end + alias_method :extension, :extension_without_delimiter + # Filename.new("foo:bar.jpg").sanitized # => "foo-bar.jpg" # Filename.new("foo/bar.jpg").sanitized # => "foo-bar.jpg" # @@ -32,6 +34,10 @@ class ActiveStorage::Filename @filename.encode(Encoding::UTF_8, invalid: :replace, undef: :replace, replace: "�").strip.tr("\u{202E}%$|:;/\t\r\n\\", "-") end + def parameters + Parameters.new self + end + # Returns the sanitized version of the filename. def to_s sanitized.to_s diff --git a/activestorage/app/models/active_storage/filename/parameters.rb b/activestorage/app/models/active_storage/filename/parameters.rb new file mode 100644 index 0000000000..58ce198d38 --- /dev/null +++ b/activestorage/app/models/active_storage/filename/parameters.rb @@ -0,0 +1,36 @@ +# frozen_string_literal: true + +class ActiveStorage::Filename::Parameters + attr_reader :filename + + def initialize(filename) + @filename = filename + end + + def combined + "#{ascii}; #{utf8}" + end + + TRADITIONAL_ESCAPED_CHAR = /[^ A-Za-z0-9!#$+.^_`|~-]/ + + def ascii + 'filename="' + percent_escape(I18n.transliterate(filename.sanitized), TRADITIONAL_ESCAPED_CHAR) + '"' + end + + RFC_5987_ESCAPED_CHAR = /[^A-Za-z0-9!#$&+.^_`|~-]/ + + def utf8 + "filename*=UTF-8''" + percent_escape(filename.sanitized, RFC_5987_ESCAPED_CHAR) + end + + def to_s + combined + end + + private + def percent_escape(string, pattern) + string.gsub(pattern) do |char| + char.bytes.map { |byte| "%%%02X" % byte }.join + end + end +end |