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

时间:2009-12-21 10:26:05

标签: ruby-on-rails ruby string filesystems

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

这样做是否有一种优雅而优雅的方式?

7 个答案:

答案 0 :(得分:58)

我想建议一个与旧的解决方案不同的解决方案。请注意,旧版本使用已弃用 returning。顺便说一下,无论如何特定于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"

我认为是你要求的。我希望这很好,也很优雅。

答案 1 :(得分:27)

来自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

答案 2 :(得分:17)

如果您使用Rails,您还可以使用String#parameterize。这不是特别适用于此,但您将获得令人满意的结果。

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

答案 3 :(得分:2)

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

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

答案 4 :(得分:2)

如果您的目标只是生成在所有操作系统上使用的“安全”文件名(而不是删除任何和所有非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"

答案 5 :(得分:0)

对于Rails,我发现自己想要保留任何文件扩展名,但对其余字符使用10*sizeof(int)

parameterize

实施细节和想法见来源: https://github.com/rails/rails/blob/master/activesupport/lib/active_support/inflector/transliterate.rb

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

答案 6 :(得分:0)

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

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