31

I often find that I have files in my projects that need to be accessed from the file system as well as the users browser. One example is uploading photos. I need access to the files on the file system so that I can use GD to alter the images or move them around. But my users also need to be able to access the files from a URL like example.com/uploads/myphoto.jpg.

Because the upload path usually corresponds to the URL I made up a function that seems to work most of the time. Take these paths for example:

File System /var/www/example.com/uploads/myphoto.jpg

URL http://example.com/uploads/myphoto.jpg

If I had a variable set to something like /var/www/example.com/ then I could subtract it from the filesystem path and then use it as the URL to the image.

/**
 * Remove a given file system path from the file/path string.
 * If the file/path does not contain the given path - return FALSE.
 * @param   string  $file
 * @param   string  $path
 * @return  mixed
 */
function remove_path($file, $path = UPLOAD_PATH) {
    if(strpos($file, $path) !== FALSE) {
        return substr($file, strlen($path));
    }
}

$file = /var/www/example.com/uploads/myphoto.jpg;

print remove_path($file, /var/www/site.com/);
//prints "uploads/myphoto.jpg"

Does anyone know of a better way to handle this?

jdphenix
  • 15,022
  • 3
  • 41
  • 74
Xeoncross
  • 55,620
  • 80
  • 262
  • 364

12 Answers12

12

More accurate way (including host port) would be to use this

function path2url($file, $Protocol='http://') {
    return $Protocol.$_SERVER['HTTP_HOST'].str_replace($_SERVER['DOCUMENT_ROOT'], '', $file);
}
George
  • 1,466
  • 3
  • 12
  • 30
  • 4
    return $Protocol.$_SERVER['HTTP_HOST'].str_replace($_SERVER['DOCUMENT_ROOT'], '', realpath($file)); Adding realpath clean up the output URL correctly - wven when the input has ../ or ./ in the path. – Stephanie Mar 24 '15 at 15:36
  • 1
    The real path should be used for the document root too `realpath($_SERVER['DOCUMENT_ROOT'])` to resolve any symbolic links if they exist. I got surprised by this today when my hosting company unexpectedly changed my www root folder to one with a symbolic link in its path, then all my string replacements abruptly stopped working. – SWHarden Jan 28 '21 at 22:47
9

Assume the directory is /path/to/root/document_root/user/file and the address is site.com/user/file

The first function I am showing will get the current file's name relative to the World Wide Web Address.

$path = $_SERVER['SERVER_NAME'] . $_SERVER['PHP_SELF'];

and would result in:

site.com/user/file

The second function strips the given path of the document root.

$path = str_replace($_SERVER['DOCUMENT_ROOT'], '', $path)

Given I passed in /path/to/root/document_root/user/file, I would get

/user/file
Tyler Carter
  • 60,743
  • 20
  • 130
  • 150
6

IMHO such automation is really error prone. You're far better off using some explicit path helpers (eg. one for uploads, one for user pics, etc) or just encapsulate for example an uploaded file with a class.

// Some "pseudo code"
$file = UploadedFile::copy($_FILES['foo']);
$file->getPath(); // /var/www/example.org/uploads/foo.ext
$file->getUri();  // http://example.org/uploads/foo.ext
Philippe Gerber
  • 17,457
  • 6
  • 45
  • 40
4

Make it easy on yourself and just define the correct locations for both the filesystem and web folders and prepend the image filename with them.

Somewhere, you'd declare:

define('PATH_IMAGES_FS', '/var/www/example.com/uploads/');
define('PATH_IMAGES_WEB', 'uploads/');

Then you can just swap between paths depending on your need:

$image_file = 'myphoto.jpg';

$file = PATH_IMAGES_FS.$image_file;
//-- stores: /var/www/example.com/uploads/myphoto.jpg

print PATH_IMAGES_WEB.$image_file;
//-- prints: uploads/myphoto.jpg
random
  • 9,774
  • 10
  • 66
  • 83
  • Yes, I actually do use constants for the main locations - but I'm afraid this won't help as I don't know the location of the file until I fetch it. In other words, most in the base UPLOAD file or URL - but they might also be in subfolders or even somewhere else. – Xeoncross Aug 07 '09 at 16:16
  • Aaaah, didn't account for working with more than one location. – random Aug 08 '09 at 06:40
  • I think this is the best approach.. Good planning makes life easier. – Popsyjunior Mar 29 '16 at 11:28
2

I've used this and worked with me:

$file_path=str_replace('\\','/',__file__);
$file_path=str_replace($_SERVER['DOCUMENT_ROOT'],'',$file_path);
$path='http://'.$_SERVER['HTTP_HOST'].'/'.$file_path;

And if you need the directory name in url format add this line:

define('URL_DIR',dirname($path));
Daban
  • 21
  • 1
  • your script might not work on other operating systems so, user DIRECTORY_SEPARATOR constant instead of explicitly it. – Popsyjunior Mar 29 '16 at 11:29
2

The code below is well commented:

function pathToURL($path) {
  //Replace backslashes to slashes if exists, because no URL use backslashes
  $path = str_replace("\\", "/", realpath($path));

  //if the $path does not contain the document root in it, then it is not reachable
  $pos = strpos($path, $_SERVER['DOCUMENT_ROOT']);
  if ($pos === false) return false;

  //just cut the DOCUMENT_ROOT part of the $path
  return substr($path, strlen($_SERVER['DOCUMENT_ROOT']));
  //Note: usually /images is the same with http://somedomain.com/images,
  //      So let's not bother adding domain name here.
}
echo pathToURL('some/path/on/public/html');
Fandi Susanto
  • 2,272
  • 1
  • 26
  • 25
2

Try this:

$imgUrl = str_replace($_SERVER['DOCUMENT_ROOT'], '', $imgPath)
Jason Plank
  • 2,336
  • 5
  • 31
  • 40
5teve23
  • 21
  • 2
1

For example, i used this one to convert C:\WAMP\WWW\myfolder\document.txt to http://example.com/myfolder/document.txt use this one:

$file_path=str_replace('\\','/',$file_path);
$file_path=str_replace($_SERVER['DOCUMENT_ROOT'],'',$file_path);
$file_path='http://'.$_SERVER['HTTP_HOST'].$file_path;
jdphenix
  • 15,022
  • 3
  • 41
  • 74
T.Todua
  • 53,146
  • 19
  • 236
  • 237
0

This simple snippet can convert the file path to file's url on the server. Some settings like protocol and port should be kept.

        $filePath = str_replace('\\','/',$filePath);
        $ssl = (!empty($_SERVER['HTTPS']) && $_SERVER['HTTPS'] == 'on') ? true : false;
        $sp = strtolower($_SERVER['SERVER_PROTOCOL']);
        $protocol = substr($sp, 0, strpos($sp, '/')) . (($ssl) ? 's' : '');
        $port = $_SERVER['SERVER_PORT'];
        $stringPort = ((!$ssl && $port == '80') || ($ssl && $port == '443')) ? '' : ':' . $port;
        $host = isset($_SERVER['HTTP_X_FORWARDED_HOST']) ? $_SERVER['HTTP_X_FORWARDED_HOST'] : isset($_SERVER['HTTP_HOST']) ? $_SERVER['HTTP_HOST'] : $_SERVER['SERVER_NAME'];
        $fileUrl = str_replace($_SERVER['DOCUMENT_ROOT'] ,$protocol . '://' . $host . $stringPort, $filePath);
0

I always use symlinks in my local development environment and @George's approach fails in this case:

The DOCUMENT_ROOT is set to /Library/WebServer/Documents and there is a symlink /Library/WebServer/Documents/repo1 -> /Users/me/dev/web/repo1

Assume that following codes are in /Users/me/dev/web/repo1/example.php

$_SERVER['DOCUMENT_ROOT'] == "/Library/WebServer/Documents" //default on OS X

while

realpath('./some/relative.file') == "/Users/me/dev/web/repo1/some/relative.file"

Thus, replacing DOCUMENT_ROOT with HTTP_HOST doesn't work.

I come up with this little trick:

function path2url($path) {
    $pos = strrpos(__FILE__, $_SERVER['PHP_SELF']);
    return substr(realpath($path), $pos);
}

// where
__FILE__                         == "/Users/me/dev/web/repo1/example.php"
$_SERVER['PHP_SELF']             ==              "/web/repo1/example.php"
realpath("./some/relative.file") == "/Users/me/dev/web/repo1/some/relative.file"
// If I cut off the pre-fix part from realpath($path), 
// the remainder will be full path relative to virtual host root
path2url("./some/relative.file") ==              "/web/repo1/some/relative.file"

I think it's good practice to fore-prevent the potential bugs even we are not likely to use symlinks in production environment.

Nandin Borjigin
  • 2,094
  • 1
  • 16
  • 37
0

All answers here promotes str_replace() which replaces all occurences anywhere in the string, not just in the beginning. preg_replace() will make sure we only do an exact match from the beginning of the string:

function remove_path($file, $path = UPLOAD_PATH) {
  return preg_replace("#^($path)#", '', $file);
}

Windows can be a problem where directory separators / and \. Make sure you replace the directory separators first:

function remove_path($file, $path = UPLOAD_PATH) {
  $file = preg_replace("#([\\\\/]+)#", '/', $file);
  $path = preg_replace("#([\\\\/]+)#", '/', $path);
  return preg_replace("#^($path)#", '', $file);
}

I would play with something like the following. Make note of realpath() and rtrim().

function webpath($file) {
  $document_root = rtrim(preg_replace("#([\\\\/]+)#", '/', $_SERVER['DOCUMENT_ROOT']), '/');
  $file = preg_replace("#([\\\\/]+)#", '/', realpath($file));
  return preg_replace("#^($document_root)#", '', $file);
}

echo webpath(__FILE__); // Returns webpath to self
echo webpath('../file.ext'); // Relative paths
echo webpath('/full/path/to/file.ext'); // Full paths
tim
  • 2,530
  • 3
  • 26
  • 45
0

One row complete solution to the "convert path to url" problem (*):

$path = "/web/htdocs/<domain>/home/path/to/file/file.ext";

$url = ( ( isset($_SERVER['HTTPS']) && ($_SERVER['HTTPS'] == 'on' || $_SERVER['HTTPS'] == 1) || isset($_SERVER['HTTP_X_FORWARDED_PROTO']) && $_SERVER['HTTP_X_FORWARDED_PROTO'] == 'https' )? 'https://' : 'http://' ).$_SERVER['HTTP_HOST'].str_replace(realpath($_SERVER['DOCUMENT_ROOT']), '', realpath($ITEM_GPX_PATH));

echo $url;

// print "http(s)://<domain>/path/to/file/file.ext"

I came to this solution thanks to the answers of George (and the respective comments of Stephanie and SWHarden) and of Rid Iculous.

Note: my solution does not answer the complete question directly, but its title; I thought to insert this answer because it can be useful to those who search on Google "php convert path to url"

Marco Panichi
  • 1,068
  • 1
  • 18
  • 31