Shortest possible encoded string with decode possibility (shorten url) using only PHP

前端 未结 13 1740
甜味超标
甜味超标 2020-12-28 19:14

I\'m looking for a method that encodes an string to shortest possible length and lets it be decodable (pure PHP, no SQL). I have working sc

13条回答
  •  伪装坚强ぢ
    2020-12-28 19:51

    EDIT

    Reading from the above and below comments, you need a solution to hide the real path of your image parser, giving it a fixed image width.

    Step 1 : http://www.example.com/tn/full/animals/images/lion.jpg

    You can achieve a basic "thumbnailer" by taking profit of .htaccess

     RewriteEngine on
     RewriteBase /
     RewriteCond %{REQUEST_FILENAME} !-f
     RewriteRule tn/(full|small)/(.*) index.php?size=$1&img=$2 [QSA,L]
    

    Your PHP file:

     $basedir="/public/content/";
     $filename=realpath($basedir.$_GET["img"]);
    
     ## check that file is in $basedir
     if ((!strncmp($filename, $basedir, strlen($basedir)) 
        ||(!file_exists($filename)) die("Bad file path");
    
     switch ($_GET["size"]) {
        case "full":
            $width=700;
            $height=500;
            ## you can also use getimagesize() to test if the image is landscape or portrait
        break;
        default:
            $width=350;
            $height=250;
        break;
     }
     ## here is your old code for resizing images
     ## Note that the "tn" directory can exist and store the actual reduced images
    

    This lets you using the url www.example.com/tn/full/animals/images/lion.jpg to view your reduced in size image.

    This has the advantage for SEO to preserve the original file name.

    Step 2 : http://www.example.com/tn/full/lion.jpg

    If you want a shorter url, if the number of images you have is not too much, you can use the basename of the file (eg. "lion.jpg") and recursively search. When collision use an index to identify which one you want (eg. "1--lion.jpg")

    function matching_files($filename, $base) {
        $directory_iterator = new RecursiveDirectoryIterator($base);
        $iterator       = new RecursiveIteratorIterator($directory_iterator);
        $regex_iterator = new RegexIterator($iterator, "#$filename\$#");
        $regex_iterator->setFlags(RegexIterator::USE_KEY);
        return array_map(create_function('$a', 'return $a->getpathName();'), iterator_to_array($regex_iterator, false));
    }
    
    function encode_name($filename) {
        $files=matching_files(basename($filename), realpath('public/content'));
        $tot=count($files);
        if (!$tot) return NULL;
        if ($tot==1) return $filename;
        return "/tn/full/".array_search(realpath($filename), $files)."--".basename($filename);
    }
    
    function decode_name($filename) {
        $i=0;
        if (preg_match("#^([0-9]+)--(.*)#", $filename, $out)) {
                $i=$out[1];
                $filename=$out[2];
        }
    
        $files=matching_files($filename, realpath('public/content'));
    
        return $files ? $files[$i] : NULL;
    }
    
    echo $name=encode_name("gallery/animals/images/lion.jp‌​g").PHP_EOL;
     ## --> returns lion.jpg
     ## You can use with the above solution the url http://www.example.com/tn/lion.jpg
    
     echo decode_name(basename($name)).PHP_EOL;
     ## -> returns the full path opn disk to the image "lion.jpg"
    

    Original post:

    Basically, if you add some formatting in your example your shorten url is in fact longer:

    img=/dir/dir/hi-res-img.jpg&w=700&h=500  // 39 chars
    y8xNt9VPySwC44xM3aLUYt3M3HS9rIJ0tXJbcwMDtQxbUwMDAA // 50 chars
    

    Using base64_encode will always result in longer strings. And gzcompress will require at less to store one occurence of the different chars; this is not a good solution for small strings.

    So doing nothing (or a simple str_rot13) is clearly the first option to consider if you want to shorten the result you had previously.

    You can also use a simple character replacement method of your choice:

     $raw_query_string = 'img=/dir/dir/hi-res-img.jpg&w=700&h=500';
     $from="0123456789abcdefghijklmnopqrstuvwxyz&=/ABCDEFGHIJKLMNOPQRSTUVWXYZ";
     // the following line if the result of str_shuffle($from)
     $to="0IQFwAKU1JT8BM5npNEdi/DvZmXuflPVYChyrL4R7xc&SoG3Hq6ks=e9jW2abtOzg";
     echo strtr($raw_query_string, $from, $to)."\n";
    
     // Result: EDpL4MEu4MEu4NE-u5f-EDp.dmprYLU00rNLA00 // 39 chars
    

    Reading from your comment, what you really want is "to prevent anyone to gets a hi-res image".

    The best way to achieve that is to generate a checksum with a private key.

    Encode:

    $secret="ujoo4Dae";
    $raw_query_string = 'img=/dir/dir/hi-res-img.jpg&w=700&h=500';
    $encoded_query_string = $raw_query_string."&k=".hash("crc32", $raw_query_string.$secret);
    

    Result: img=/dir/dir/hi-res-img.jpg&w=700&h=500&k=2ae31804

    Decode:

    if (preg_match("#(.*)&k=([^=]*)$#", $encoded_query_string, $out)
        && (hash("crc32", $out[1].$secret) == $out[2])) {
        $decoded_query_string=$out[1];
    }
    

    This does not hide the original path but this path has no reason to be public, your "index.php" can output your image from the local directory once the key has been checked.

    If you really want to shorten your original URL, you have to consider the acceptable characters in the original url to be restricted. Many compression methods are based on the fact that you can use a full byte to store more than a character.

提交回复
热议问题