我正在寻找一个将清理字符串并准备用于文件名的php函数。有人知道吗?
(我会写一个,但是我担心自己会忽略一个字符!)
编辑:用于将文件保存在Windows NTFS文件系统上。
我正在寻找一个将清理字符串并准备用于文件名的php函数。有人知道吗?
(我会写一个,但是我担心自己会忽略一个字符!)
编辑:用于将文件保存在Windows NTFS文件系统上。
Answers:
不用担心忽略字符-如何使用您愿意使用的字符白名单?例如,你可以让刚刚好醇” a-z
,0-9
,_
,和一段时间的单个实例(.
)。这显然比大多数文件系统更具限制性,但应该可以确保您的安全。
对Tor Valamo的解决方案进行小幅调整以解决Dominic Rodger注意到的问题,您可以使用:
// Remove anything which isn't a word, whitespace, number
// or any of the following caracters -_~,;[]().
// If you don't need to handle multi-byte characters
// you can use preg_replace rather than mb_ereg_replace
// Thanks @Łukasz Rysiak!
$file = mb_ereg_replace("([^\w\s\d\-_~,;\[\]\(\).])", '', $file);
// Remove any runs of periods (thanks falstro!)
$file = mb_ereg_replace("([\.]{2,})", '', $file);
..
。例如.?.
最终会被..
。尽管自从进行过滤后,/
我现在看不到如何进一步利用它,但是它说明了为什么检查..
在这里无效。更好的可能是,不要替换,如果不符合条件就拒绝。
[^a-z0-9_-]
如果您想成为真正的限制-或仅使用生成的名称并丢弃给定名称,避免所有这些问题。:-)
这是您可以按照要求清理文件系统的方法
function filter_filename($name) {
// remove illegal file system characters https://en.wikipedia.org/wiki/Filename#Reserved_characters_and_words
$name = str_replace(array_merge(
array_map('chr', range(0, 31)),
array('<', '>', ':', '"', '/', '\\', '|', '?', '*')
), '', $name);
// maximise filename length to 255 bytes http://serverfault.com/a/9548/44086
$ext = pathinfo($name, PATHINFO_EXTENSION);
$name= mb_strcut(pathinfo($name, PATHINFO_FILENAME), 0, 255 - ($ext ? strlen($ext) + 1 : 0), mb_detect_encoding($name)) . ($ext ? '.' . $ext : '');
return $name;
}
文件系统中允许其他所有内容,因此可以完美回答问题。
...,但是如果稍后在不安全的HTML上下文中使用文件名,则在文件名中使用单引号可能会很危险,'
因为该文件名绝对合法:
' onerror= 'alert(document.cookie).jpg
成为XSS漏洞:
<img src='<? echo $image ?>' />
// output:
<img src=' ' onerror= 'alert(document.cookie)' />
因此,流行的CMS软件Wordpress删除了它们,但是它们仅在进行一些更新后才涵盖所有相关字符:
$special_chars = array("?", "[", "]", "/", "\\", "=", "<", ">", ":", ";", ",", "'", "\"", "&", "$", "#", "*", "(", ")", "|", "~", "`", "!", "{", "}", "%", "+", chr(0));
// ... a few rows later are whitespaces removed as well ...
preg_replace( '/[\r\n\t -]+/', '-', $filename )
最后,他们的列表现在包括URI保留字符和URL不安全字符列表中的大多数字符。
当然,您可以简单地在HTML输出上对所有这些字符进行编码,但是大多数开发人员和我也都遵循成语“比对不起更好”,并提前删除它们。
所以最后我建议使用这个:
function filter_filename($filename, $beautify=true) {
// sanitize filename
$filename = preg_replace(
'~
[<>:"/\\|?*]| # file system reserved https://en.wikipedia.org/wiki/Filename#Reserved_characters_and_words
[\x00-\x1F]| # control characters http://msdn.microsoft.com/en-us/library/windows/desktop/aa365247%28v=vs.85%29.aspx
[\x7F\xA0\xAD]| # non-printing characters DEL, NO-BREAK SPACE, SOFT HYPHEN
[#\[\]@!$&\'()+,;=]| # URI reserved https://tools.ietf.org/html/rfc3986#section-2.2
[{}^\~`] # URL unsafe characters https://www.ietf.org/rfc/rfc1738.txt
~x',
'-', $filename);
// avoids ".", ".." or ".hiddenFiles"
$filename = ltrim($filename, '.-');
// optional beautification
if ($beautify) $filename = beautify_filename($filename);
// maximize filename length to 255 bytes http://serverfault.com/a/9548/44086
$ext = pathinfo($filename, PATHINFO_EXTENSION);
$filename = mb_strcut(pathinfo($filename, PATHINFO_FILENAME), 0, 255 - ($ext ? strlen($ext) + 1 : 0), mb_detect_encoding($filename)) . ($ext ? '.' . $ext : '');
return $filename;
}
不会导致文件系统出现问题的其他所有内容都应该是附加功能的一部分:
function beautify_filename($filename) {
// reduce consecutive characters
$filename = preg_replace(array(
// "file name.zip" becomes "file-name.zip"
'/ +/',
// "file___name.zip" becomes "file-name.zip"
'/_+/',
// "file---name.zip" becomes "file-name.zip"
'/-+/'
), '-', $filename);
$filename = preg_replace(array(
// "file--.--.-.--name.zip" becomes "file.name.zip"
'/-*\.-*/',
// "file...name..zip" becomes "file.name.zip"
'/\.{2,}/'
), '.', $filename);
// lowercase for windows/unix interoperability http://support.microsoft.com/kb/100625
$filename = mb_strtolower($filename, mb_detect_encoding($filename));
// ".file-name.-" becomes "file-name"
$filename = trim($filename, '.-');
return $filename;
}
此时,如果结果为空,则需要生成一个文件名,然后可以决定是否要编码UTF-8字符。但是您不需要这样做,因为在Web托管上下文中使用的所有文件系统中都允许使用UTF-8。
唯一要做的就是使用urlencode()
(希望对所有URL都使用),以便文件名საბეჭდი_მანქანა.jpg
成为该URL作为您的<img src>
或<a href>
:http :
//www.maxrev.de/html/img/%E1%83% A1%E1%83%90%E1%83%91%E1%83%94%E1%83%AD%E1%83%93%E1%83%98_%E1%83%9B%E1%83%90% E1%83%9C%E1%83%A5%E1%83%90%E1%83%9C%E1%83%90.jpg
Stackoverflow会这样做,因此我可以像用户那样发布此链接:http :
//www.maxrev.de/html/img/საბეჭდი_მანქანა。jpg
所以这是一个完整的法律文件名和不是一个问题,因为@ SequenceDigitale.com在他的回答中提到。
r-u-l-e-s
,我不知道为什么会发生这种情况。可以肯定的是,这不是该功能的错,而只是问-这种行为的原因是什么?编码错误?
使用rawurlencode()怎么样? http://www.php.net/manual/zh/function.rawurlencode.php
这是一个甚至可以清除中文字符的函数:
public static function normalizeString ($str = '')
{
$str = strip_tags($str);
$str = preg_replace('/[\r\n\t ]+/', ' ', $str);
$str = preg_replace('/[\"\*\/\:\<\>\?\'\|]+/', ' ', $str);
$str = strtolower($str);
$str = html_entity_decode( $str, ENT_QUOTES, "utf-8" );
$str = htmlentities($str, ENT_QUOTES, "utf-8");
$str = preg_replace("/(&)([a-z])([a-z]+;)/i", '$2', $str);
$str = str_replace(' ', '-', $str);
$str = rawurlencode($str);
$str = str_replace('%', '-', $str);
return $str;
}
这是解释
好的,某些文件名不会令人反感,但在大多数情况下它将起作用。
例如 原始名称:“საბეჭდი-და-ტიპოგრაფიული。jpg”
输出名称:“-E1-83-A1-E1-83-90-E1-83-91-E1-83-94-E1-83-AD-E1-83-93-E1-83-98--E1- 83-93-E1-83-90--E1-83-A2-E1-83-98-E1-83-9E-E1-83-9D-E1-83-92-E1-83-A0-E1-83 -90-E1-83-A4-E1-83-98-E1-83-A3-E1-83-9A-E1-83-98.jpg”
像这样比404错误更好。
希望对您有所帮助。
卡尔
http://www.maxrev.de/html/img/საბეჭდი_მანქანა.jpg
到http://www.maxrev.de/html/img/%E1%83%A1%E1%83%90%E1%83%91%E1%83%94%E1%83%AD%E1%83%93%E1%83%98_%E1%83%9B%E1%83%90%E1%83%9C%E1%83%A5%E1%83%90%E1%83%9C%E1%83%90.jpg
HTML源代码,您希望您的所有网址做。
strip_tags()
和之后都删除[<>]
。到此为止,strip_tags()
根本不需要。引号是同一点。使用解码时,没有引号了ENT_QUOTES
。并且str_replace()
不会删除连续的空格,然后将其strtolower()
用于多字节字符串。为什么要完全转换为小写?最后,您没有抓住@BasilMusa提到的任何保留字符。我的答案中有更多详细信息:stackoverflow.com/a/42058764/318765
解决方案1-简单有效
$file_name = preg_replace( '/[^a-z0-9]+/', '-', strtolower( $url ) );
[^a-z0-9]+
将确保文件名仅保留字母和数字'-'
保持文件名可读例:
URL: http://stackoverflow.com/questions/2021624/string-sanitizer-for-filename
File: http-stackoverflow-com-questions-2021624-string-sanitizer-for-filename
解决方案2-网址很长
您要缓存URL内容,只需要具有唯一的文件名。我将使用此功能:
$file_name = md5( strtolower( $url ) )
这将创建一个固定长度的文件名。在大多数情况下,MD5哈希对于这种用法而言足够独特。
例:
URL: https://www.amazon.com/Interstellar-Matthew-McConaughey/dp/B00TU9UFTS/ref=s9_nwrsa_gw_g318_i10_r?_encoding=UTF8&fpl=fresh&pf_rd_m=ATVPDKIKX0DER&pf_rd_s=desktop-1&pf_rd_r=BS5M1H560SMAR2JDKYX3&pf_rd_r=BS5M1H560SMAR2JDKYX3&pf_rd_t=36701&pf_rd_p=6822bacc-d4f0-466d-83a8-2c5e1d703f8e&pf_rd_p=6822bacc-d4f0-466d-83a8-2c5e1d703f8e&pf_rd_i=desktop
File: 51301f3edb513f6543779c3a5433b01c
好吧,tempnam()会为您做到这一点。
http://us2.php.net/manual/zh/function.tempnam.php
但这创造了一个全新的名称。
要清理现有字符串,只需限制用户可以输入的内容,并使其成为字母,数字,句点,连字符和下划线,然后使用简单的正则表达式清理即可。检查哪些字符需要转义,否则您可能得到误报。
$sanitized = preg_replace('/[^a-zA-Z0-9\-\._]/','', $filename);
preg_replace("[^\w\s\d\.\-_~,;:\[\]\(\]]", '', $file)
根据系统允许的内容添加/删除更多有效字符。
另外,您可以尝试创建文件,如果不好则返回错误。
..
,这可能会或可能不会出现问题。
PHP提供了将文本清除为其他格式的功能
如何 :
echo filter_var(
"Lorem Ipsum has been the industry's",FILTER_SANITIZE_URL
);
块引用
LoremIpsumhasbeentheindustry's
以下表达式创建一个漂亮,干净且可用的字符串:
/[^a-z0-9\._-]+/gi
谈到今天的金融:开票到今天-S-金融结算
preg_replace
全局标志是隐式的。因此,如果正在使用preg_replace,则不需要g。当我们想控制替换次数时,preg_replace有一个limit
参数。阅读preg_replace文档以获取更多信息。
这些可能有点沉重,但是它们足够灵活,可以将任何字符串消毒为“安全”字符串 en
样式的文件名或文件夹名称(或者弯曲,甚至弯曲的条和其他东西)。
1)构建完整的文件名(如果输入被完全截断,则具有备用名称):
str_file($raw_string, $word_separator, $file_extension, $fallback_name, $length);
2)或仅使用筛选器实用程序而不构建完整的文件名(严格模式true
将不允许在文件名中使用[]或()):
str_file_filter($string, $separator, $strict, $length);
3)这些是这些功能:
// Returns filesystem-safe string after cleaning, filtering, and trimming input
function str_file_filter(
$str,
$sep = '_',
$strict = false,
$trim = 248) {
$str = strip_tags(htmlspecialchars_decode(strtolower($str))); // lowercase -> decode -> strip tags
$str = str_replace("%20", ' ', $str); // convert rogue %20s into spaces
$str = preg_replace("/%[a-z0-9]{1,2}/i", '', $str); // remove hexy things
$str = str_replace(" ", ' ', $str); // convert all nbsp into space
$str = preg_replace("/&#?[a-z0-9]{2,8};/i", '', $str); // remove the other non-tag things
$str = preg_replace("/\s+/", $sep, $str); // filter multiple spaces
$str = preg_replace("/\.+/", '.', $str); // filter multiple periods
$str = preg_replace("/^\.+/", '', $str); // trim leading period
if ($strict) {
$str = preg_replace("/([^\w\d\\" . $sep . ".])/", '', $str); // only allow words and digits
} else {
$str = preg_replace("/([^\w\d\\" . $sep . "\[\]\(\).])/", '', $str); // allow words, digits, [], and ()
}
$str = preg_replace("/\\" . $sep . "+/", $sep, $str); // filter multiple separators
$str = substr($str, 0, $trim); // trim filename to desired length, note 255 char limit on windows
return $str;
}
// Returns full file name including fallback and extension
function str_file(
$str,
$sep = '_',
$ext = '',
$default = '',
$trim = 248) {
// Run $str and/or $ext through filters to clean up strings
$str = str_file_filter($str, $sep);
$ext = '.' . str_file_filter($ext, '', true);
// Default file name in case all chars are trimmed from $str, then ensure there is an id at tail
if (empty($str) && empty($default)) {
$str = 'no_name__' . date('Y-m-d_H-m_A') . '__' . uniqid();
} elseif (empty($str)) {
$str = $default;
}
// Return completed string
if (!empty($ext)) {
return $str . $ext;
} else {
return $str;
}
}
因此,假设一些用户输入是: .....<div></div><script></script>& Weiß Göbel 中文百强网File name %20 %20 %21 %2C Décor \/. /. . z \... y \...... x ./ “This name” is & 462^^ not = that grrrreat -][09]()1234747) საბეჭდი-და-ტიპოგრაფიული
我们希望将其转换为更友好的名称,以使tar.gz的文件名长度为255个字符。这是一个示例用法。注意:此示例包含格式错误的tar.gz扩展名作为概念证明,在根据白名单构建字符串之后,您仍应过滤ext。
$raw_str = '.....<div></div><script></script>& Weiß Göbel 中文百强网File name %20 %20 %21 %2C Décor \/. /. . z \... y \...... x ./ “This name” is & 462^^ not = that grrrreat -][09]()1234747) საბეჭდი-და-ტიპოგრაფიული';
$fallback_str = 'generated_' . date('Y-m-d_H-m_A');
$bad_extension = '....t&+++a()r.gz[]';
echo str_file($raw_str, '_', $bad_extension, $fallback_str);
输出为: _wei_gbel_file_name_dcor_._._._z_._y_._x_._this_name_is_462_not_that_grrrreat_][09]()1234747)_.tar.gz
您可以在这里玩:https : //3v4l.org/iSgi8
或要点:https : //gist.github.com/dhaupin/b109d3a8464239b7754a
编辑: 更新脚本过滤器
代替空间,更新3v4l链接
我今天所知道的最好的是静态方法Strings :: webalize Nette框架中的。
顺便说一句,这会将所有变音符号转换成它们的基本符号。š=> sü=> uß=> ss等
对于文件名,您必须添加点“。” 允许的字符参数。
/**
* Converts to ASCII.
* @param string UTF-8 encoding
* @return string ASCII
*/
public static function toAscii($s)
{
static $transliterator = NULL;
if ($transliterator === NULL && class_exists('Transliterator', FALSE)) {
$transliterator = \Transliterator::create('Any-Latin; Latin-ASCII');
}
$s = preg_replace('#[^\x09\x0A\x0D\x20-\x7E\xA0-\x{2FF}\x{370}-\x{10FFFF}]#u', '', $s);
$s = strtr($s, '`\'"^~?', "\x01\x02\x03\x04\x05\x06");
$s = str_replace(
array("\xE2\x80\x9E", "\xE2\x80\x9C", "\xE2\x80\x9D", "\xE2\x80\x9A", "\xE2\x80\x98", "\xE2\x80\x99", "\xC2\xB0"),
array("\x03", "\x03", "\x03", "\x02", "\x02", "\x02", "\x04"), $s
);
if ($transliterator !== NULL) {
$s = $transliterator->transliterate($s);
}
if (ICONV_IMPL === 'glibc') {
$s = str_replace(
array("\xC2\xBB", "\xC2\xAB", "\xE2\x80\xA6", "\xE2\x84\xA2", "\xC2\xA9", "\xC2\xAE"),
array('>>', '<<', '...', 'TM', '(c)', '(R)'), $s
);
$s = @iconv('UTF-8', 'WINDOWS-1250//TRANSLIT//IGNORE', $s); // intentionally @
$s = strtr($s, "\xa5\xa3\xbc\x8c\xa7\x8a\xaa\x8d\x8f\x8e\xaf\xb9\xb3\xbe\x9c\x9a\xba\x9d\x9f\x9e"
. "\xbf\xc0\xc1\xc2\xc3\xc4\xc5\xc6\xc7\xc8\xc9\xca\xcb\xcc\xcd\xce\xcf\xd0\xd1\xd2\xd3"
. "\xd4\xd5\xd6\xd7\xd8\xd9\xda\xdb\xdc\xdd\xde\xdf\xe0\xe1\xe2\xe3\xe4\xe5\xe6\xe7\xe8"
. "\xe9\xea\xeb\xec\xed\xee\xef\xf0\xf1\xf2\xf3\xf4\xf5\xf6\xf8\xf9\xfa\xfb\xfc\xfd\xfe"
. "\x96\xa0\x8b\x97\x9b\xa6\xad\xb7",
'ALLSSSSTZZZallssstzzzRAAAALCCCEEEEIIDDNNOOOOxRUUUUYTsraaaalccceeeeiiddnnooooruuuuyt- <->|-.');
$s = preg_replace('#[^\x00-\x7F]++#', '', $s);
} else {
$s = @iconv('UTF-8', 'ASCII//TRANSLIT//IGNORE', $s); // intentionally @
}
$s = str_replace(array('`', "'", '"', '^', '~', '?'), '', $s);
return strtr($s, "\x01\x02\x03\x04\x05\x06", '`\'"^~?');
}
/**
* Converts to web safe characters [a-z0-9-] text.
* @param string UTF-8 encoding
* @param string allowed characters
* @param bool
* @return string
*/
public static function webalize($s, $charlist = NULL, $lower = TRUE)
{
$s = self::toAscii($s);
if ($lower) {
$s = strtolower($s);
}
$s = preg_replace('#[^a-z0-9' . preg_quote($charlist, '#') . ']+#i', '-', $s);
$s = trim($s, '-');
return $s;
}
urlencode()
在将文件名用作src
或之前只需使用即可href
。目前唯一使用的UTF-8出现问题的文件系统是FATx(由XBOX使用):en.wikipedia.org/wiki/Comparison_of_file_systems#Limits而且我不认为Web服务器会使用此文件系统
单程
$bad='/[\/:*?"<>|]/';
$string = 'fi?le*';
function sanitize($str,$pat)
{
return preg_replace($pat,"",$str);
}
echo sanitize($string,$bad);
/
并且..
用户提供的文件名可能有害。因此,您应该通过以下方法消除这些问题:
$fname = str_replace('..', '', $fname);
$fname = str_replace('/', '', $fname);
..name
不会破任何东西的字符串。删除所有路径分隔符应足以防止任何目录遍历。(从..
技术上讲,删除是不必要的。)
./.
变为..
。最后,这个答案错过了所有其他文件系统保留的字符,例如NULL。我的答案中的更多信息:stackoverflow.com/a/42058764/318765
$ fname = str_replace('/','',$ fname);
由于用户可能使用斜杠分隔两个单词,因此最好用破折号代替NULL代替