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

我有用户输入作为文件名。 当然,这不是一个好主意,所以我想删除除[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

有没有一种不错且优雅的方法来做到这一点?

marcgg asked 2020-07-29T13:19:06Z
6个解决方案
60 votes

我想提出一种不同于旧解决方案的解决方案。 请注意,旧的使用已弃用的2987872463834834579968。顺便说一句,它始终是特定于Rails的,并且您没有在问题中明确提及Rails(仅作为标记)。 另外,现有解决方案无法按照您的要求将Z编码为a。 而且,当然,它不会将下划线分解为一个。

这是我的解决方案:

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

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

  • 文件扩展名最多应有一个,这意味着文件名中最多应有一个句点
  • 尾随期间不标记扩展名的开始
  • 前导期不表示扩展的开始
  • 超出AZaz09-的任何字符序列都应折叠成单个_(即,下划线本身被认为是不允许的字符,而2987872463则是2987872463而不是2987872463字符串) '$%''__''°#'

复杂的部分是我将文件名分为主要部分和扩展名。 在正则表达式的帮助下,我正在搜索最后一个句号,其后是除句号以外的其他内容,这样就不会有随后的句号与字符串中的相同条件匹配。 但是,必须在它前面加上一些字符,以确保它不是字符串中的第一个字符。

我从测试该功能的结果:

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

我认为这是您的要求。 我希望这足够好和优雅。

Anders Sjöqvist answered 2020-07-29T13:20:23Z
28 votes

来自[http://devblog.muziboo.com/2008/06/17/attachment-fu-sanitize-filename-regex-and-unicode-gotcha/:]

def sanitize_filename(filename)
  returning filename.strip 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
miku answered 2020-07-29T13:19:21Z
17 votes

如果使用Rails,也可以使用String#parameterize。 这并不是专门为此目的,但是您将获得令人满意的结果。

"my§document$is°°   very&interesting___thisIs%nice445.doc.pdf".parameterize
albandiguer answered 2020-07-29T13:20:43Z
3 votes

在Rails中,您还可以使用ActiveStorage :: Filename中的sanitize

ActiveStorage::Filename.new("foo:bar.jpg").sanitized # => "foo-bar.jpg"
ActiveStorage::Filename.new("foo/bar.jpg").sanitized # => "foo-bar.jpg"
morgler answered 2020-07-29T13:21:03Z
1 votes

对于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
Blair Anderson answered 2020-07-29T13:21:28Z
0 votes

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

irb(main):001:0> require 'unidecoder'
=> true
irb(main):004:0> "Grzegżółka".to_ascii
=> "Grzegzolka"
Jan Warchoł answered 2020-07-29T13:21:49Z
translate from https://stackoverflow.com:/questions/1939333/how-to-make-a-ruby-string-safe-for-a-filesystem