在PHP中转换字符串以用作文件名...而不剥离特殊字符

Our current system saves files that match the name of their parent group. So a group named "My Devices" saves an image called "My+Devices.jpg". This worked great, and even though there was some trouble with the name getting converted somewhere along the line of PHP and JS to "My%20Devices.jpg", I was able to just convert spaces to plus signs before that happened.

Now we need to account for other special characters, like apostrophes so that a group can be named "Joe's Devices". I need some way to convert that to something that can be a file name. BUT I can't just strip out the special characters or there could be a collision if someone uses a group name of "Joes Devices" (without the apostrophe).

I tried urlencode() and rawurlencode(), but those use percent signs, which are great for URLs but not so much for file names. I thought base64_encode() would be better even though it's a much longer string. But it includes equal signs which are no good for filenames.

Is there a way to convert a string to a filename-friendly string, one that can be decoded back to it's original string? Or do I need to recode this feature completely and use an ID match or something?

Your quest is very similar to questions around creating url-safe base64 encoded strings.

See this answer for one example: https://stackoverflow.com/a/5835352/660694

My solution would be a variation of this.

In one of my apps I have this wrapper function:

function base64_safe_encode($input, $stripPadding = false)
{
    $encoded = strtr(base64_encode($input), '+/=', '-_~');

    return ($stripPadding) ? str_replace("~","",$encoded) : $encoded;
}

Now you can generate a filename or url safe base64 encoded string that is easily reversible.

Note there is an option to strip the padding at the end of the string if you like, PHP does not require the padding characters to be present when you decode.

The decode function is then quite simple:

function base64_safe_decode($input)
{
    return base64_decode(strtr($input, '-_~', '+/='));
}

Lets say you have a group named 'My Device'. You then hash the name of it with md5 to save it in a database or something:

$group = 'My Device';
$filename = md5($group) . '.png';

To display or work with it, you just hash the group name again wherever you want:

<?php
$group = 'My Device';
$filename = md5($group) . '.png';
?>
<img src="<?php print $filename; ?>" />