How can I change wordpress attachment url and protect it?

I’ve figured out a solution, here is my answer

1, we can’t modify WordPress original data input, but we can modify and rewrite the output. So we can use the_content,wp_get_attachment_url to change this.

add this code snippets into your plugins

    if (function_exists('add_filter')) {

        add_filter('the_content', array($scplugin, 'AttachmentRewrite'), 10, 1);
        add_filter('wp_get_attachment_url', array($scplugin, 'AttachmentUrlRewrite'), 10, 1);
    }

the ‘$scplugin’ is my plugin class. ‘the_content’ filter will pass a “the content” string into ‘AttachmentRewrite’ function. so does the ‘wp_get_attachment_url’, but it will pass a url string.

then we can modify and rewrite the content url link by regex.

here is the AttachmentRewrite function code snippets:

/**
 * Rewrite the content attachment links.
 *
 * @param string $content theContent.
 *
 * @return mix.
 */
public function AttachmentRewrite($content)
{
    $pattern = "/<a href=\"(\S+)(\/wp-content\/uploads\/sites\/)([0-9]+)(\S+)\">/";
    preg_match_all($pattern, $content, $match);

    // $match[0]  all links
    // $match[1] link head  http://xxx/network site subpath
    // $match[2] replace content /wp-content/uploads/sites/
    // $match[3] site num
    // $match[4] file meta_value
    if (isset($match) && is_array($match)) {
        $post_id = get_the_ID();
        $old_links = array();
        $new_links = array();
        foreach ($match[1] as $k => $v) {
            $old_links[$k] = $v.$match[2][$k].$match[3][$k].$match[4][$k];
            $checkMedia = $this->QueryMediaMeta($match[3][$k], $match[4][$k]);

            if (!$checkMedia) {
                $new_links[$k] = $old_links[$k];
            } else {
                if (isset($checkMedia['post_type']) && $checkMedia['post_type'] == 'attachment' && isset($checkMedia['post_mime_type']) ) {
                    $checkApp = strpos($checkMedia['post_mime_type'], 'application');

                    if ($checkApp === false) {
                        $new_links[$k] = $old_links[$k];
                    } else {
                        $_snx_id = $this->UpdateAttachUrl($match[3][$k], $match[4][$k]);
                        $rand_string = $this->RandString(10);
                        // s=site no, id=snx attachment_id, aid=post type attachment type id
                        $param = $rand_string.base64_encode('s=".$match[3][$k]."&id='.$_snx_id.'&p='.$post_id);
                        // return $match[1].'/download.php?s=".$match[3][$k]."&id='.$_snx_id;
                        $new_links[$k] = $v.'/download.php?fid='.$param;
                    }
                } else {
                    $new_links[$k] = $old_links[$k];
                }
            }
            //$new_links[$k] = array_walk($_head_link, array($this, '_array_walks')).'?download.php?s=".$match[3][$k]."&id='.$match[4][$k];
        }
        array_walk($old_links, array($this, 'snxArrayWalks'));
        $content = preg_replace($old_links, $new_links, $content);
    }

    return $content;
}

I use preg_match_all to match all attachment url, including the media(mp3,jpeg,gif). then we wget the match data, but we will filter it again for the mp3,jpeg,gif etc, so the function QueryMediaMeta will query wp_posts and wp_post_meta by filename and site num to get this.

Then I use a RandString and base64_encode to translate the original url. and return the new the_content data.

In the download.php, you can control the access right, define forbidden page. etc.