[SOLVED] How to display Protected Images?

Good afternoon all.

I’m attempting to render images from a protected folder under the Yii hierarchy. Has anyone successfully done this?

Setup:

Images are stored in: webroot.protected.upload.images. Would like to display images from a controller view.

It would be great (if possible) to render images via the html tag “img src=” (or equivalent) as I’m trying to display multiple images formatted nicely with other html on one page.

My research here on Yii turned up CAssetsManager which I would like to avoid because the images are not protected once published (from my understanding):

http://www.yiiframework.com/forum/index.php?/topic/15867-solved-images-under-protected-folder/

The most common response I’ve discovered is to use readfile(), but I am having no luck in getting it to work. The methods I’ve tried via readfile() are:

  1. Created a controller function called from a view using CController::forward. No luck.

  2. Created a ‘lone’ php file in the same directory as the view file. I could not get this to work.

  3. Created a component, registered it and called it from the view. That seems to be the most promising route, but I get the error: "The image cannot be displayed because it contains errors.". The image I have is a simple jpg.

Here is the code:




class getimg {


       public function init()

        {


	}


	public function readImage($filename)

        {


            $path=Yii::getPathOfAlias('webroot.protected.upload.images') . '/';

            $file=$path.$filename;


            if (file_exists($file))

            {

                header('Content-Type: image/jpeg');

                header('Content-Length: ' . filesize($file));

                readfile($file);

             }

            else

            {

                 return "";

            }


	}


}



And then called from a view via:




echo "<img src='".Yii::app()->getimg->readImage($filename)."'>";



I’ve also tried to call the function outside of the img tag, but the results are the same. Any help is most appreciated.

Thanks!

Try x-sendfile.

Thanks mentel, however It’s not exactly what I’m looking for. Please correct me If I’m wrong, but xSendFile will actually download the image file (instead of rendering it in the browser). I’m actually using this logic elsewhere in my application.

I’ve stumbled upon “Content-Disposition: inline”, but the results are the same. Any other clues? Is there no way to render a protected image in the browser via Yii?

If you have a


<img>

element in your document, the browser will make another request for the image.

In that request, it will be downloaded and then rendered by the browser. Unless there are some HTTP headers or the MIME type is incorrect, the browser will not ask the user to save the file and everything will work.

Hmm, here is the code given $file is correct and exists (and it does).


echo "<img src='".CHttpRequest::xSendFile($file)."'>";

The code is from a view containing other HTML elements (tables, lists, etc), nothing out of the ordinary. At this section of code, I’m not setting headers or MIME type. Is this incorrect?

The above code produces the error "The image cannot be displayed because it contains errors."

The xSendFile() is so wrong to be used in this context. This methods, will read a file and will append the needed headers to be DOWNLOADED not RENDERED to user.

The approach:




echo "<img src='".Yii::app()->getimg->readImage($filename)."'>";



Is also wrong, because what it does, is to read and return an image, and you pass it to a img src tag, which is used to look after a file path.

The right way to do this, although your approach of putting the files in a protected location makes no sense is this:

  1. Create a controller that takes a single argument, the file name or the file ID.

  2. Based on the argument, determine the entire image path on the server

  3. use readfile() (i believe file_get_contents() also will do it just fine) or something similar to read the file from your server into a variable

  4. append the right headers for your image(warning, don’t append DOWNLOAD headers, just the CONTENT TYPE)

  5. echo the result of the readfile OR file_get_contents

now having these set, in your views, just do like:




<img src="controller-that-handles-the-protected-images/the-name-of-the-file.png" />



Now, let’s recap what’s happening after the src call of the image:

  1. The browser sees the src tag, and executes a request to get the image

  2. the request goes to your controller that expects a param being the file name or file id.

  3. the controller finds out the full path of the requested image

  4. the controller reads the image and appends right headers

  5. the controller shows the image .

See, very easy when you know what you want .

But my advice is just to put your images in a public place accessible by everyone .

Is way easier this way .

L.E:

I forgot to mention that, once your controller resolves the entire file path, you can use getimagesize() to find out the correct mime-type of the image in order to add the correct headers.

Thanks twisted1919, I actually tried this as my first approach to a solution… The result was the same, "The image cannot be displayed because it contains errors." And file_get_contents produces the same exact error. Any other ideas?

You are not adding the correct headers, that’s why you get that error.

And the correct headers are what exactly? I’ve already reduced the headers (from my original post) down to content type only.

Let’s try this way, before you add the headers, do a :

$img=getimagesize($file);

then add the headers like :

header('Content-Type: '.$img[‘mime’]);

readfile($file);

exit;

And see what is happening, also, please paste here the controller method and the way you call the image .

Thanks twisted1919, here is my code. I know more is needed for a dynamic $filename, but I’. merely trying to get the basics working first. The error remains the same.

Controller Action:




public function actionGetFigureImage()

        {

            $filename = "test.jpg";

            $path=Yii::getPathOfAlias('webroot.protected.upload.images') . '/';

            $file=$path.$filename;


            if (file_exists($file))

            {

                $img=getimagesize($file);


                //header('Content-Type: image/jpeg');

                header('Content-Type: '.$img['mime']);

                readfile($file);

            }

}



View call:




CController::forward('mycontroller/getfigureimage', false);



Not sure that it matters, but I’ve tried assigning the controller action to a variable and nested within an img tag, same results. What am I doing wrong?

Add the exit; call after readfile() call. The script needs to end right after that .

So, try like this




public function actionGetFigureImage()

        {

            $filename = "test.jpg";

            $path=Yii::getPathOfAlias('webroot.protected.upload.images') . '/';

            $file=$path.$filename;


            if (file_exists($file))

            {

                $img=getimagesize($file);

                header('Content-Type: '.$img['mime']);

                readfile($file);

                exit;

            }

}



If that won’t work:




public function actionGetFigureImage()

        {

            $filename = "test.jpg";

            $path=Yii::getPathOfAlias('webroot.protected.upload.images') . '/';

            $file=$path.$filename;


            if (file_exists($file))

            {

                header('Content-Type: image/jpeg');

                readfile($file);

                exit;

            }

}



Also, access this controller method VIA BROWSER directly.

http://mydomain/controller/method

Accessing the action from the browser works using any version of the code (exit, no exit, img[‘mime’], etc). So, the question now is can the image be rendered within a view or no? From within the view, the error remains.

Use the src="" tag to look for the image from within the view. it’s so obvious.

In the src you put same url that you added in the browser bar and shown your image.

Ah yes, too obvious. That’s what happens when staring at a problem to long. Thanks twisted1919, its working like a champ.

Glad that after all it is solved :)




/**

 	* generates thumb of image

 	* @param string $img - path of image

	*/

	public function actionViewImages($img)

	{


    	$folder = Yii::getPathOfAlias('images_path') . DIRECTORY_SEPARATOR;

    	$path = $folder . $img;


    	$img = Yii::app()->imagemod->load($path);


        	$img->file_new_name_body = md5($img->file_src_name);


    	header('Content-type: ' . $img->file_src_mime);

    	echo $img->process();

	}



also i was using this extension cimagemodifier

Shame on me. I didn’t read all of the original post and didn’t notice the

Glad that it is solved.

how to use it with fancybox…?

i had this problem to, i was not abel to fix it before i added ob_clen(), like:




            if (file_exists($file))

            {

		ob_clean();

                header('Content-Type: image/jpeg');


                readfile($file);

                exit;

            }