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

mar*_*cgg 43 ruby filesystems string ruby-on-rails

我有用户条目作为文件名.当然,这不是一个好主意,所以我想放下一切,除了[a-z],[A-Z],[0-9],_-.

例如:

my§document$is°°   very&interesting___thisIs%nice445.doc.pdf
Run Code Online (Sandbox Code Playgroud)

应该成为

my_document_is_____very_interesting___thisIs_nice445_doc.pdf
Run Code Online (Sandbox Code Playgroud)

然后理想情况下

my_document_is_very_interesting_thisIs_nice445_doc.pdf
Run Code Online (Sandbox Code Playgroud)

这样做有一种漂亮而优雅的方式吗?

And*_*ist 60

我想建议一个与旧的解决方案不同的解决方案.请注意,旧的使用已弃用的 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
Run Code Online (Sandbox Code Playgroud)

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

  • 最多应该有一个文件扩展名,这意味着文件名中最多只能有一个句点
  • 尾随期间不标记扩展的开始
  • 领先期间不标记延期的开始
  • 任何字符序列超出A- Z,a- z,0- 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"
Run Code Online (Sandbox Code Playgroud)

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


mik*_*iku 28

来自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
Run Code Online (Sandbox Code Playgroud)

  • `name.gsub!(/ [^ 0-9A-Za-z.\ - ] /,'_')`是我5年后用过的唯一部分:D (4认同)
  • 如果不执行替换,不会使用`gsub!`导致函数返回`nil`?如果是这样,现在是否需要将gsub'd字符串的值赋值给新变量并在返回任何内容之前测试nil? (4认同)

alb*_*uer 17

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

"my§document$is°°   very&interesting___thisIs%nice445.doc.pdf".parameterize
Run Code Online (Sandbox Code Playgroud)


mor*_*ler 13

在 Rails 中,您也可以sanitizeActiveStorage::Filename 使用

ActiveStorage::Filename.new("foo:bar.jpg").sanitized # => "foo-bar.jpg"
ActiveStorage::Filename.new("foo/bar.jpg").sanitized # => "foo-bar.jpg"
Run Code Online (Sandbox Code Playgroud)

  • 这很性感。我之前使用过“参数化”,但它有点过于严厉,会删除“安全”字符,例如空格和与号。但这并不能做到这一点。例如 `ActiveStorage::Filename.new("foo:bar &amp; baz.jpg").sanitized #=&gt; "foo-bar &amp; baz.jpg"`。好的!另外,您可以轻松添加一个初始化程序,该初始化程序会修补 `String` 并添加一个 `String#sanitized` 方法,该方法本质上只是调用此方法,因此您可以执行类似 `"foo:bar &amp; baz.jpg".sanitized #=&gt; 的操作“foo-bar 和 baz.jpg”`。性感死了。 (2认同)