You are here

public function S3fsStream::getExternalUrl in S3 File System 8.2

Same name and namespace in other branches
  1. 8.3 src/StreamWrapper/S3fsStream.php \Drupal\s3fs\StreamWrapper\S3fsStream::getExternalUrl()
  2. 4.0.x src/StreamWrapper/S3fsStream.php \Drupal\s3fs\StreamWrapper\S3fsStream::getExternalUrl()

Returns a web accessible URL for the resource.

The format of the returned URL will be different depending on how the S3 integration has been configured on the S3 File System admin page.

Return value

string A web accessible URL for the resource.

Overrides StreamWrapperInterface::getExternalUrl

File

src/StreamWrapper/S3fsStream.php, line 347

Class

S3fsStream
Defines a Drupal s3fs (s3fs://) stream wrapper class.

Namespace

Drupal\s3fs\StreamWrapper

Code

public function getExternalUrl() {

  //$this->_debug("getExternalUrl() called for {$this->uri}.");

  //$path = str_replace('\\', '/', $this->getTarget());

  //return $GLOBALS['base_url'] . '/' . self::getDirectoryPath() . '/' . UrlHelper::encodePath($path);

  // In case we're on Windows, replace backslashes with forward-slashes.
  // Note that $uri is the unaltered value of the File's URI, while
  // $s3_key may be changed at various points to account for implementation
  // details on the S3 side (e.g. root_folder, s3fs-public).
  $s3_key = $uri = str_replace('\\', '/', file_uri_target($this->uri));

  // If this is a private:// file, it must be served through the
  // system/files/$path URL, which allows Drupal to restrict access
  // based on who's logged in.
  if (\Drupal::service('file_system')
    ->uriScheme($this->uri) == 'private') {

    // Convert backslashes from windows filenames to forward slashes.
    $path = str_replace('\\', '/', $uri);
    $relative_url = Url::fromUserInput("/system/files/{$path}");
    return Link::fromTextAndUrl($relative_url, $relative_url);

    //return url("system/files/$path", array('absolute' => TRUE));
  }

  // When generating an image derivative URL, e.g. styles/thumbnail/blah.jpg,
  // if the file doesn't exist, provide a URL to s3fs's special version of
  // image_style_deliver(), which will create the derivative when that URL
  // gets requested.
  $path_parts = explode('/', $uri);
  if ($path_parts[0] == 'styles' && substr($uri, -4) != '.css') {
    if (!$this
      ->_s3fs_get_object($this->uri)) {
      $args = $path_parts;
      array_shift($args);
      $style = array_shift($args);
      $scheme = array_shift($args);
      $filename = implode('/', $args);
      $original_image = "{$scheme}://{$filename}";

      // Load the image style configuration entity.
      $style = ImageStyle::load($style);
      $destination = $style
        ->buildUri($original_image);
      $style
        ->createDerivative($original_image, $destination);
    }
  }

  // Deal with public:// files.
  if (\Drupal::service('file_system')
    ->uriScheme($this->uri) == 'public') {

    // Rewrite all css/js file paths unless the user has told us not to.
    if (!$this->config['no_rewrite_cssjs']) {
      if (substr($uri, -4) == '.css') {

        // Send requests for public CSS files to /s3fs-css/path/to/file.css.
        // Users must set that path up in the webserver config as a proxy into
        // their S3 bucket's s3fs-public/ folder.
        return "{$GLOBALS['base_url']}/s3fs-css/" . UrlHelper::encodePath($uri);
      }
      else {
        if (substr($uri, -3) == '.js') {

          // Send requests for public JS files to /s3fs-js/path/to/file.js.
          // Like with CSS, the user must set up that path as a proxy.
          return "{$GLOBALS['base_url']}/s3fs-js/" . UrlHelper::encodePath($uri);
        }
      }
    }

    // public:// files are stored in S3 inside the s3fs_public_folder.
    $public_folder = !empty($this->config['public_folder']) ? $this->config['public_folder'] : 's3fs-public';
    $s3_key = "{$public_folder}/{$s3_key}";
  }

  // Set up the URL settings as speciied in our settings page.
  $url_settings = [
    'torrent' => FALSE,
    'presigned_url' => FALSE,
    'timeout' => 60,
    'forced_saveas' => FALSE,
    'api_args' => [
      'Scheme' => !empty($this->config['use_https']) ? 'https' : 'http',
    ],
    'custom_GET_args' => [],
  ];

  // Presigned URLs.
  foreach ($this->presignedURLs as $blob => $timeout) {

    // ^ is used as the delimeter because it's an illegal character in URLs.
    if (preg_match("^{$blob}^", $uri)) {
      $url_settings['presigned_url'] = TRUE;
      $url_settings['timeout'] = $timeout;
      break;
    }
  }

  // Forced Save As.
  foreach ($this->saveas as $blob) {
    if (preg_match("^{$blob}^", $uri)) {
      $filename = basename($uri);
      $url_settings['api_args']['ResponseContentDisposition'] = "attachment; filename=\"{$filename}\"";
      $url_settings['forced_saveas'] = TRUE;
      break;
    }
  }

  // If a root folder has been set, prepend it to the $s3_key at this time.
  if (!empty($this->config['root_folder'])) {
    $s3_key = "{$this->config['root_folder']}/{$s3_key}";
  }
  if (empty($this->config['use_cname'])) {

    // We're not using a CNAME, so we ask S3 for the URL.
    $expires = NULL;
    if ($url_settings['presigned_url']) {
      $expires = "+{$url_settings['timeout']} seconds";
    }
    else {

      // Due to Amazon's security policies (see Request client
      // eters section @
      // http://docs.aws.amazon.com/AmazonS3/latest/API/RESTObjectGET.html),
      // only signed requests can use request parameters.
      // Thus, we must provide an expiry time for any URLs which specify
      // Response* API args. Currently, this only includes "Forced Save As".
      foreach ($url_settings['api_args'] as $key => $arg) {
        if (strpos($key, 'Response') === 0) {
          $expires = "+10 years";
          break;
        }
      }
    }
    $external_url = $this->s3
      ->getObjectUrl($this->config['bucket'], $s3_key, $expires, $url_settings['api_args']);
    if (!empty($this->config['presigned_urls'])) {
      foreach (explode(PHP_EOL, $this->config['presigned_urls']) as $line) {
        $blob = trim($line);
        if ($blob) {
          $presigned_url_parts = explode("|", $blob);
          if (preg_match("^{$presigned_url_parts[1]}^", $s3_key) && $expires) {
            $command = $this->s3
              ->getCommand('GetObject', [
              'Bucket' => $this->config['bucket'],
              'Key' => $s3_key,
            ]);
            $external_url = $this->s3
              ->createPresignedRequest($command, $expires);
            $uri = $external_url
              ->getUri();
            $external_url = $uri
              ->__toString();
          }
        }
      }
    }
  }
  else {

    // We are using a CNAME, so we need to manually construct the URL.
    $external_url = "{$this->domain}/{$s3_key}";
  }

  // If this file is versioned, append the version number as a GET arg to
  // ensure that browser caches will be bypassed upon version changes.
  $meta = $this
    ->_read_cache($this->uri);
  if (!empty($meta['version'])) {
    $external_url = $this
      ->_append_get_arg($external_url, $meta['version']);
  }

  // Torrents can only be created for publicly-accessible files:
  // https://forums.aws.amazon.com/thread.jspa?threadID=140949
  // So Forced SaveAs and Presigned URLs cannot be served as torrents.
  if (!$url_settings['forced_saveas'] && !$url_settings['presigned_url']) {
    foreach ($this->torrents as $blob) {
      if (preg_match("^{$blob}^", $uri)) {

        // You get a torrent URL by adding a "torrent" GET arg.
        $external_url = $this
          ->_append_get_arg($external_url, 'torrent');
        break;
      }
    }
  }

  // If another module added a 'custom_GET_args' array to the url settings, process it here.
  if (!empty($url_settings['custom_GET_args'])) {
    foreach ($url_settings['custom_GET_args'] as $name => $value) {
      $external_url = $this
        ->_append_get_arg($external_url, $name, $value);
    }
  }
  return $external_url;
}