如何使Ruby字符串对文件系统安全?

uinbv5nw  于 2023-06-29  发布在  Ruby
关注(0)|答案(7)|浏览(109)

我有用户条目作为文件名。当然,这不是一个好主意,所以我想删除除了[a-z][A-Z][0-9]_-之外的所有内容。
例如:

my§document$is°°   very&interesting___thisIs%nice445.doc.pdf

应该成为

my_document_is_____very_interesting___thisIs_nice445_doc.pdf

然后理想情况下

my_document_is_very_interesting_thisIs_nice445_doc.pdf

有没有一种优雅的方式来做这件事?

pvabu6sv

pvabu6sv1#

我想提出一个不同于旧的解决办法。请注意,旧版本使用deprecatedreturning。顺便说一句,它是特定于Rails的,而且你没有在你的问题中明确提到Rails(只是作为一个标签)。此外,现有的解决方案无法按照您的要求将.doc.pdf编码为_doc.pdf。当然,它不会将下划线折叠成一个。
我的解决方案是:

def sanitize_filename(filename)
  # Split the name when finding a period which is preceded by some
  # character, and is followed by some character other than a period,
  # if there is no following period that is followed by something
  # other than a period (yeah, confusing, I know)
  fn = filename.split /(?<=.)\.(?=[^.])(?!.*\.[^.])/m

  # We now have one or two parts (depending on whether we could find
  # a suitable period). For each of these parts, replace any unwanted
  # sequence of characters with an underscore
  fn.map! { |s| s.gsub /[^a-z0-9\-]+/i, '_' }

  # Finally, join the parts with a period and return the result
  return fn.join '.'
end

您尚未指定有关转换的所有详细信息。因此,我做了以下假设:

  • 最多应该有一个文件扩展名,这意味着文件名中最多应该有一个句点
  • 尾随期间不标记扩展的开始
  • 前置句点不标记扩展的开始
  • 任何超过A-Za-z0-9-的字符序列都应该折叠成一个_(即下划线本身被认为是不允许的字符,字符串'$%__°#'将变为'_'-而不是'___'(由'$%''__''°#'组成)

复杂的部分是我把文件名分成主要部分和扩展名。在正则表达式的帮助下,我正在搜索最后一个句点,它后面跟的不是句点,这样在字符串中就没有匹配相同条件的句点了。但是,它必须在某个字符之前,以确保它不是字符串中的第一个字符。
我测试函数的结果:

1.9.3p125 :006 > sanitize_filename 'my§document$is°°   very&interesting___thisIs%nice445.doc.pdf'
 => "my_document_is_very_interesting_thisIs_nice445_doc.pdf"

我想这是你要求的我希望这足够漂亮和优雅。

ao218c7q

ao218c7q2#

http://web.archive.org/web/20110529023841/http://devblog.muziboo.com/2008/06/17/attachment-fu-sanitize-filename-regex-and-unicode-gotcha/

def sanitize_filename(filename)
  filename.strip.tap do |name|
   # NOTE: File.basename doesn't work right with Windows paths on Unix
   # get only the filename, not the whole path
   name.gsub!(/^.*(\\|\/)/, '')

   # Strip out the non-ascii character
   name.gsub!(/[^0-9A-Za-z.\-]/, '_')
  end
end
pengsaosao

pengsaosao3#

在Rails中,你也可以使用ActiveStorage::Filename#sanitized

ActiveStorage::Filename.new("foo:bar.jpg").sanitized # => "foo-bar.jpg"
ActiveStorage::Filename.new("foo/bar.jpg").sanitized # => "foo-bar.jpg"
eni9jsuy

eni9jsuy4#

如果你使用Rails,你也可以使用String#parameterize。这并不是特别为这个目的而设计的,但你会得到一个令人满意的结果。

"my§document$is°°   very&interesting___thisIs%nice445.doc.pdf".parameterize
46scxncf

46scxncf5#

对于Rails,我发现自己想要保留任何文件扩展名,但使用parameterize作为其余字符:

filename = "my§doc$is°° very&itng___thsIs%nie445.doc.pdf"
cleaned = filename.split(".").map(&:parameterize).join(".")

实现细节和思路参见源代码:https://github.com/rails/rails/blob/master/activesupport/lib/active_support/inflector/transliterate.rb

def parameterize(string, separator: "-", preserve_case: false)
  # Turn unwanted chars into the separator.
  parameterized_string.gsub!(/[^a-z0-9\-_]+/i, separator)
  #... some more stuff
end
daupos2t

daupos2t6#

如果您的目标只是生成一个在所有操作系统上都“安全”使用的文件名(并且不删除任何和所有非ASCII字符),那么我推荐zaru gem。它并没有完成原始问题指定的所有操作,但是生成的文件名应该可以安全使用(并且仍然保持任何文件名安全的unicode字符不变):

Zaru.sanitize! "  what\ēver//wëird:user:înput:"
# => "whatēverwëirduserînput"
Zaru.sanitize! "my§docu*ment$is°°   very&interes:ting___thisIs%nice445.doc.pdf" 
# => "my§document$is°° very&interesting___thisIs%nice445.doc.pdf"
z9ju0rcb

z9ju0rcb7#

有一个库可能会有帮助,特别是如果你有兴趣用ASCII替换奇怪的Unicode字符:unidecode

irb(main):001:0> require 'unidecoder'
=> true
irb(main):004:0> "Grzegżółka".to_ascii
=> "Grzegzolka"

相关问题