Austin Kregel
Austin Kregel

Reputation: 755

Getting an image to render with php

To keep it simple, I'm trying to display an image using php to help try and prevent any kind of a security loophole. I have the image being read through a php script and I am wanting to render it, so instead of going to http://example.com/path/to/uploads/dir/image.jpg I want to have the user be able to go to http://example.com/path/to/script/image1 and have them be able to see the image.

This way I can hide the image path (http://example.com/path/to/uploads/dir/) outside my document root (to where it can't be outright accessed by the domain path). So if the path to the domain was /path/to/public_html/ then I want the images uploaded to /path/to/uploads/ so you can't navigate to the uploads dir when surfing my domain.

My issue is the image I have been trying to render, won't render and I'm unsure as to why. The MD5 checksums are the same, and the HTTP headers are similar. The direct path to the image, compared to the desired path to the image.

I am using Laravel as a framework / backbone, but I don't think that matters to much, and my code to render the image is below

public function getImage($id){
  $img = TitanImage::findOrFail($id);
//  header('Content-type: image/jpeg');
//  header('Content-Length: ' . filesize($img->path));
//  header('Accept-Ranges: bytes');
//  echo file_get_contents($img->path);
  $file = $img->path;
  $type = 'image/jpeg';
  header('Content-Type:'.$type);
  header('Content-Length: ' . filesize($file));
  readfile($file);
//  exit();
  return;
}

I also used webconfs to check the headers of both of the desired image endpoints.

Edit 1

In regards to one of the comments about the intervention/image package, I changed my code and I still get the same result; however, the content-length has changed, it's gotten longer.

public function getImage($id){
  $img = TitanImage::findOrFail($id);
//  header('Content-type: image/jpeg');
//  header('Content-Length: ' . filesize($img->path));
//  header('Accept-Ranges: bytes');
  $file = $img->path;
  return Image::make(trim($file,' '))->response();
}

Edit 2

In this edit, @Digitlimit mentioned checking to see if the image exists. This is my current code.

public function getImage($id){
  $img = TitanImage::findOrFail($id);
  $file = $img->path;
  if(file_exists($file))
    return Image::make($file)->response('jpg');
  else
    return 'File doesnt exist';
}

I also did test to see what would happen if I were to just return $file instead of the Image::make instance, I get the proper path, ie. when I go to SSH into the server and cd to the path minus the image name the path exists and has permissions. When I do the ls -la command it lists the image and it also has the proper permissions.

Upvotes: 1

Views: 1368

Answers (2)

Emeka Mbah
Emeka Mbah

Reputation: 17553

If you have installed intervention image then do this:

public function getImage($id){
  $img = TitanImage::findOrFail($id);
  $file = $img->path;
  return Image::make($file)->response('jpg');
}

Edited: I tested this on my project and it works perfectly:

Route:

Route::get('/api/v1/titan/image/{id}',function($id){
    return \Image::make(storage_path("/images/$id.jpg"))->response('jpg');
});

Image location in my project:

enter image description here

Output when I visit URL:http://laravel.dev/api/v1/titan/image/1

enter image description here

Chrome Element Inspection enter image description here

enter image description here

Upvotes: 0

LSerni
LSerni

Reputation: 57418

The code is working - the problem is it's sending extra information. The file is there, it is found, it is being sent. The link to your image returns:

HTTP/1.1 200 OK
Date: Wed, 17 Jun 2015 22:52:03 GMT
Server: Apache
Connection: close
Content-Type: image/jpeg

But the subsequent output is wrong:

00000050  3a 20 63 6c 6f 73 65 0d  0a 43 6f 6e 74 65 6e 74  |: close..Content|
00000060  2d 54 79 70 65 3a 20 69  6d 61 67 65 2f 6a 70 65  |-Type: image/jpe|
00000070  67 0d 0a 0d 0a 20 ff d8  ff e1 19 57 45 78 69 66  |g.... .....WExif|
                         ^^
                           `this 20 here, before the ff d8 ff of JPEG.

So this code (being only responsible for the JPEG part) is OK.

public function getImage($id){
  $img    = TitanImage::findOrFail($id);
  $file   = $img->path;
  if (file_exists($file)) {
      return Image::make($file)->response('jpg');
  }
  else {
    return "File doesn't exist";
  }
}

The extra space at the beginning of the data stream might come from some PHP file with an extra whitespace before the opening <?php tag, or maybe after the closing tag.

For the latter, it is recommended that you do not close the <?php tag, i.e. you never include the "?>" sequence.

For the former, try

public function getImage($id) {
    return "*** is there a space before the asterisks?";
}

and observe the returned HTML. Unless the error is in TitanImage, which I believe unlikely, you should see the same space before the asterisks that is sent before the JPEG image. The file where the getImage() function is defined is a likely suspect.

Otherwise, you can run "grep" to find PHP files that do not start with <?php. You can modify this shameless plug by adding the -v flag to grep.

Upvotes: 2

Related Questions