I'm trying to find the way to convert an uri like public://directory/file.ext to a relative path sites/default/files/directory/file.ext (of course this is an example because public:// could have other path) and I can't find the correct function in file API.

How to do it?

Thanks.

Comments

Damien Tournoud’s picture

Something like that will work:

$scheme = file_uri_scheme($file);

if ($scheme && file_stream_wrapper_valid_scheme($scheme)) {
  $wrapper = file_stream_wrapper_get_instance_by_scheme($scheme);
  $path = $wrapper->getLocalPath($file);
}

But it will only work if the stream wrapper defines getLocalPath() (only public and private do that currently).

manfer’s picture

I'm going to try to explain a little more because I'm not sure to understand if that is what I need.

The module where I need to use that relative path is a module that has on its configuration form upload file form elements. So it uploads the files and I have those file objects. I have to store on database and finally on the module output I need the url of the file to send it as a variable to a flash swf file. It is not valid for me an uri with schema (public://directory/file.ext) as output. And as it is a variable for flash, and I want to preserve the security on that flash that only allows local files, an absolute url like, http://example.com/sites/default/files/directory/file.ext, as returned by file_create_url, is not valid for me either.

On 6.x I was storing the filepath of the file object, $file->filepath, and that was fine for what I needed. It was something as sites/default/files/directory/file.ext, so on output I was using basepath() . check_url(filepath) and was the correct url I needed.

On 7.x I'm not sure how to proceed, I would like to continue storing the filepath, and proceed the same on output, but now in the file object I only have uri, and uri is public://directory/file.ext. (schema is going to be public always, public://directory is a constant on the module, is the directory where I upload the files). But I don't know how to create that relative filepath, how to obtain sites/default/files/directory/file.ext (or whatever it be, if public:// is other path. Example if public:// is only files, files/directory/file.ext).

manfer’s picture

Status: Closed (fixed) » Fixed

The absolute path returned by file_create_url is just fine to be used in flash vars and it is correctly in the same security sandbox. I pass it through urlencode before sending it to flash vars.

The problem I was experimenting was that I was doing a file move to the file object and I thought the file object uri was going to be updated with the new uri but it is not (I was storing then an incorrect uri), though the documentation says: "Move a file to a new location and update the file's database entry."

Reading that I was understanding the file object passed by reference and being updated but looking at the code it is not so, a new file object is returned.

As I don't really need the module uploaded files in database (I only need the files uploaded and uri stored on module table), I'm going to change code to unmanaged versions of file functions. Probably uploading files to temp and an unmanaged copy to my public://directory destination, then storing that uri, is the best solution for me.

Status: Active » Closed (fixed)

Automatically closed -- issue fixed for 2 weeks with no activity.

lmeurs’s picture

Hi,

In Drupal 7/8 one can use

drupal_realpath($uri);

See http://api.drupal.org/api/drupal/includes--file.inc/function/drupal_real...

knalstaaf’s picture

Can you provide an example of this practice please?

This doesn't work for me:

<?php 
  $file_path = drupal_realpath($uri); 
?>

(in a node.tpl.php)

It results in

Notice: Array to string conversion in file_uri_scheme() (line 204 of /var/www/html/includes/file.inc).
Notice: Array to string conversion in drupal_realpath() (line 2190 of /var/www/html/includes/file.inc).
knalstaaf’s picture

<a href="<?php print drupal_realpath($video_path); ?>>Watch video</a> works, but now the url looks like "/var/www/html/sites/default/files/flv/video-eng.flv". How do I get rid of /var/www/html/?

(#1 doesn't work either in my case.)

nevets’s picture

Status: Closed (fixed) » Active

Use file_create_url() instead of drupal_real_path()?

knalstaaf’s picture

Thanks, that works but it includes the domain name (http://www.domain.com/sites/all/files/video.flv). Is it possible just to strip it down to sites/all/files/video.flv ? Or does that require extra coding?

13rac1’s picture

Probably extra coding. Why does it matter if the domain name is listed? For most uses full URLs are better.

Everett Zufelt’s picture

Status: Active » Fixed

Status: Fixed » Closed (fixed)

Automatically closed -- issue fixed for 2 weeks with no activity.

Anly Dcouth’s picture

Status: Fixed » Closed (fixed)

Hi,

I have a similar issue. I want to categorize all the files according to their formats like jpg,jpeg files in Image folder; flv,mov in Videos folder and css file sin Css folder etc. Can you pls guide me in doing so..
In simple way...
I want my public:// -> sites/default/files to change to sites/default/files/DigitalLibray/Images for all image format files.
is there any module or do i ahve to add php code in the file .

Max_Headroom’s picture

TravisCarden’s picture

Re #9: parse_url() would get you what you're actually after:

$url = file_create_url($field[0]['uri']);
$url = parse_url($url);
$path = $url['path'];
knalstaaf’s picture

Exactly! Thanks!!

sirajs’s picture

Some additional notes about parse_url.
Assuming the uri you are currently getting looks something like this: public://user1/node24/somefile.ext .... in example #15 $url=parse_url($url) returns an array with three elements:

$url['scheme'] = 'public'
$url['host'] = 'user1/'
$url['path'] = 'node24/somefile.ext'
hannanxp’s picture

Try this..

$wrapper = file_stream_wrapper_get_instance_by_uri($uri);
  $path = $wrapper->getDirectoryPath() . "/" . file_uri_target($uri);
  
  print $path;
robhoefakker’s picture

Title: How can I convert a file uri to relative path? » Visa versa
Version: 7.x-dev » 7.15

This is the solution if you're trying to get the alias for a taxonomy term id (tid). In this case i'm using this in a view template, so $row['tid'] is the term id you want your alias for.

  $argtax_search = "taxonomy/term/".$row['tid'];
  $result = db_query('SELECT alias
  FROM url_alias WHERE source = :source', array(':source' => $argtax_search));

  foreach ($result as $record) {
       $part = $record->alias;
  }
  
  $args2 = explode('/', $part);
  $alias = $args2[1];
robhoefakker’s picture

Title: Visa versa » How can I convert a file uri to relative path?

My bad, changed the title.

nevets’s picture

It would be better to use the API

$alias = drupal_get_path_alias("taxonomy/term/".$row['tid']);

Though if one is building a link, it is better to use the l() function [For a url use url()], both will take care of aliasing for you.

fedik’s picture

I use for "public" (:

$path = str_replace('public:/', variable_get('file_public_path', 'sites/default/files'), $uri);
Gerben Zaagsma’s picture

I am trying to do this too but where should I implement the snippets mentioned above. For example #22 in my theme template? Or settings.php? Forgive my ignorance but I am not a coder.

fedik’s picture

in place where you need the relative path

Gerben Zaagsma’s picture

thanks!

merzikain’s picture

I've seen a ton of answers and talking in circles but haven't really seen a good, universal answer to the original question:

How can I convert a file uri to relative path?

The answer is incredibly simple and should fit ANY use case:

$realpath = drupal_realpath($uri);
$path = str_replace($_SERVER['DOCUMENT_ROOT'].'/','',$realpath);

Not every stream uri is going to be public:// or private://

I have custom modules that define their own stream wrappers and I'm certain other people do to. Using the above should work for any and all stream wrappers you encounter.

Example:
Say you have a stream wrapper for library:// that goes to files in {drupal_root}/sites/all/libraries

And say you have $uri = library://some_library/some_folder/some_image.jpg

Then $realpath = drupal_realpath($uri); makes $realpath = /var/www/html/drupal/sites/all/libraries/some_library/some_folder/some_image.jpg

And then $path = str_replace($_SERVER['DOCUMENT_ROOT'].'/','',$realpath); makes $path = sites/all/libraries/some_library/some_folder/some_image.jpg

Which means you end up with a relative path from a stream wrapper uri with very little code or processing overhead from loading unnecessary objects and such.

Nicolas Bouteille’s picture

Issue summary: View changes

#27 worked great thanks !

Altcom_Alan’s picture

#27 doesn't work on Windows as the slashes are different eg. drupal_realpath() returns "C:\folder\folder\file.ext" and $_SERVER['DOCUMENT_ROOT'] is set as "C:/folder" so no replacement happens.

Here's my take on how to create a relative path from a uri:

    global $base_url;
    $url = file_create_url($uri);
    $url = str_replace($base_url .'/', '', $url);
Nicolas Bouteille’s picture

switched to #29 so that it can work on windows too. Works great too. Thanks.

samsterlin’s picture

<?php
     global $base_url;
     $file_path = str_replace('public:/', variable_get('file_public_path', 'sites/default/files'),$uri);
     $realpath = $base_url . '/' . $file_path;
?>
Gik000’s picture

I can't believe it should be so hard to get a **** image from public:// url type! ...

Anyway $base_url will give the URL not the path!

kenorb’s picture

David_Rothstein’s picture

Version: 7.15 » 7.x-dev
Status: Closed (fixed) » Fixed

For the original question, most of the answers here aren't correct in the general case. Using file_create_url() doesn't work because the URL returned by that function doesn't even have to be on the same server (think YouTube videos, CDNs, etc), and using $_SERVER['DOCUMENT_ROOT'] only works on certain server configurations (plus doesn't work at all for anything that would normally be stored outside the document root, such as private:// or temporary://)...

  1. If you read the above and think "I don't care about the general case, I just want this to work for public files" then there's no need to mess with stream wrappers and such. This should work fine:
      $path = variable_get('file_public_path', conf_path() . '/files') . '/' . file_uri_target($uri);
    
  2. If you need something that works more generally then @hannanxp's solution in #18 is the correct one - but with the caveat that it won't work (and might even give a fatal error) for non-local files (again, think YouTube videos). So the most robust solution is probably something like this (small modification to @hannanxp's solution):
      $wrapper = file_stream_wrapper_get_instance_by_uri($uri);
      if ($wrapper instanceof DrupalLocalStreamWrapper) {
        $path = $wrapper->getDirectoryPath() . '/' . file_uri_target($uri);
      }
      else {
        // This does not appear to be a local file so there's no such thing as
        // a relative path; do appropriate error handling here.
      }
    

    As described in the code comment, for files which aren't stored locally the original question (and the whole concept of "relative path") doesn't even apply, so that's what makes this a bit complicated in the general case.

Status: Fixed » Closed (fixed)

Automatically closed - issue fixed for 2 weeks with no activity.

dshumaker’s picture

Just a side note / tid-bit for someone out there.

If you are using this code in say, a template file, then you might want to check the existence of the file before you "print" it. However the results of #34 David's code returns code that passes the file_exists php function but does not resolve correctly without a forward slash. Let my code explain:

$block_data = entity_metadata_wrapper('field_collection_item', $block);
$img = $block_data->field_image->value();
  if ($img) {
    //$file_uri = file_create_url($img['uri']);
    // Don't use file_create_url because file_exists fails on results
    
    // #34 David's code
    $file = variable_get('file_public_path', conf_path() . '/files') . '/' . file_uri_target($img['uri']);
    
    // $file = '/' .  variable_get('file_public_path', conf_path() . '/files') . '/' . file_uri_target($img['uri']);   
    // Can't do this because file_exists fails with preceding slash however the slash is needed for rendering.

if (file_exists($file)) {
      list($width, $height, $type, $attr) = getimagesize($file);
    }
    $output .= '<div class="vertical_performance" style="min-height:'
      . $height
      . 'px"><a title="'
      . $img['title']
      . '" class="fancybox-img" href="/'
      . $file
      . '"><img id="'
      . $img['fid']
      . '" src="/'
      . $file
      . '" title="'
      . $img['title']
      . '" alt="'
      . $img['alt']
      . '"></a></div>';
  }

print $output;

So, notice the forward slashes added to the src and href string output lines above, and how I tried to add the forward slash to the file string passed to file_exists but that failed. The final result listed works.

Hopefully helpful.

-d

mforbes’s picture

In case anyone stumbled here looking to make root-relative resource paths for things like img src values, here's some info. #36 above is almost there, but adding an initial "/" is not good in all cases because the whole site could be in a subdirectory of the docroot, like "http://example.com/my-site/". Therefore, we need to prepend "/my-site/" and not just "/". But how to find the string "/my-site/" (and just "/" if the site lives at the docroot)?

Let's say the file is "public://images/foo/jpg" so the path internal to the site is "sites/default/files/images/foo.jpg" and the root-relative path we desire is "/my-site/sites/default/files/images/foo.jpg".

Well, #15 above is great! The concern raised in #17 is mistaken because we are not passing "public://images/foo.jpg" to parse_url(), rather we are passing the output of file_create_url("public://images/foo.jpg"), so the 'path' element of the returned array will be just what we need. If you look at the code for file_create_url(), it basically just prepends $GLOBALS['base_url'] . '/' which in our case would be "http://example.com/my-site/", so then the remaining task is to lop off the "http://example.com" and parse_url() is the tool for the job:

$url = file_create_url('public://images/foo.jpg');
$url = parse_url($url);
return $url['path'];

It would be nice if we could bypass the absolute URL (with the scheme/host/port/etc) in the first place and just discover that "/my-site/" prefix alone. Fortunately, that's precisely what $GLOBALS['base_path'] is! (Aside: Complementing that is $GLOBALS['base_root'] which would just be "http://example.com".) So we could start with that and append the internal URL, which is to say append the file_public_path followed by the part after the "public://" (which can be stripped off trivially). Finding the file_public_path ("sites/default/files") is given in #22, #31, and #34 above, but #34 seems to have the best fallback so that's what I'll go with:

$pub = variable_get('file_public_path', conf_path() . '/files');
$file = 'images/foo.jpg';
return $GLOBALS['base_path'] . $pub . '/' . $file;

I'm not sure if the first way or the second way is "better." Both methods are naive in the case of external URLs as #34 mentions, and the only way I can think of to really plan for that would be to see if $GLOBALS['base_url'] is at the start of the string returned by file_create_url() like so:

$abs_url = file_create_url('public://images/foo.jpg');
if (strpos($abs_url, $GLOBALS['base_url']) === 0) {
  $parsed = parse_url($abs_url);
  return $parsed['path'];
}
else {
  return $abs_url;
}

If anybody has any comments on these methods, or an an even more "correct" way of getting a root-relative path, please chime in. My particular use-case is taking advantage of the fact that mimemail embeds images into multipart email messages only when the path is non-absolute, and the only non-absolute format that also renders reliably in a web browser is root-relative, so I basically need root-relative img tags if I want web previews of those email messages.

jdleonard’s picture

Here's what worked for my needs. When added to mymodule.module (don't forget to clear the cache), this modifies all files using the public:// scheme to use relative paths.

/**
 * Implements hook_file_url_alter().
 */
function mymodule_file_url_alter(&$uri) {
  global $base_path;

  $scheme = file_uri_scheme($uri);

  // If the file uses the public scheme,
  if ($scheme == 'public') {
    $wrapper = file_stream_wrapper_get_instance_by_scheme($scheme);
    // Get the file's path
    $path = $wrapper->getDirectoryPath() . '/' . file_uri_target($uri);
    // Set the URL to the (relative) base path plus the file's path
    $uri = $base_path . $path;
  }
}
doitDave’s picture

Thanks, parse_url() was the missing link.

rahul_sankrit’s picture

@jdleonard: Thanks, I tried this and it is working good for images but not working for Js and CSS files.

Change the Base URL:
Set the global variable called $base_url in sites/default/settings.php.

SivaprasadC’s picture

Comment #29 Works great to me. Thanks.