Controller Action To Dipslay Images Returns Http 0.9/200 Ok

Hello,

I’m having some problem displaying images… it’s for a portfolio website, the user can upload images, they’re stored on the filesystem and the path is stored in the database.

I have a PortfolioImage model, a ThumbImage model, and the portfolioController with this action (added some logging…) :




/**

   * Displays the thumb of an image

   * @param integer $id the ID of the image from which the thumb should be displayed.

   */

  public function actionThumb($id)

  {

    $model=$this->loadImage($id);

    if($model->thumb){

      if(!is_file($model->thumb->filepath)) {

        error_log('not found');

    }


    if(!is_readable($model->thumb->filepath)) {

      error_log('not readable');

    }

    error_log('ok, reading thumb '.$model->thumb->filepath);

      header("Content-Type: ". $model->thumb->mime);

      header("Content-Length: " . $model->thumb->filesize);

      //ob_clean();

      //flush();

      

      echo file_get_contents($model->thumb->filepath);

      exit;

    } else {

      // return full size image

      $this->redirect(array('portfolio/image', 'id'=>$id));

    }

  }



And I display all the images in the portfolio in my view as follow :


<?php echo CHtml::image(Yii::app()->createUrl('portfolio/thumb', array('id'=>$data->id)), null, array('width'=>$data->thumb->width, 'height'=>$data->thumb->height) ); ?>

Basically it tries to load the thumb of the image, if not found it loads the main image. It works, I can see the image thumb, there’s no problem with the model/controller. But as soon as I reach 8 images, I experience some strange problems…

I currently have 11 images. The first 8 images are correctly loaded and displayed. Then it hangs for a couple of seconds and I can see in the log that the last 3 images are loaded. But some of them don’t show in the browser (not always the same). For these failing images I can see in Chrome developer tools that I’m getting a response header HTTP 0.9/200 OK. But it’s empty.

I’ve tried different method to read and display the images (readfile, file_get_contents, fopen/fpassthru, I always get the same problem. And it’s not always the same images… but I never get all the images to display correctly, whatever I do (I get between 6 and 8 images ok, not always the same…).

I can’t see any error in apache nor php logs… here are the php error_log with the trace, you can see the 5 seconds gap after the first 8 images :


25-Oct-2013 10:35:45 Europe/Berlin] ok, reading thumb /Users/fab/code/architectural-render-app/web/protected/config/../../uploads/19/portfolio/thumbs/525cf1538e01a_IMG_7381.JPG

[25-Oct-2013 10:35:45 Europe/Berlin] ok, reading thumb /Users/fab/code/architectural-render-app/web/protected/config/../../uploads/19/portfolio/thumbs/525cf1b89405d_IMG_1082.JPG

[25-Oct-2013 10:35:45 Europe/Berlin] ok, reading thumb /Users/fab/code/architectural-render-app/web/protected/config/../../uploads/19/portfolio/thumbs/525cf1567e818_IMGP1042.JPG

[25-Oct-2013 10:35:45 Europe/Berlin] ok, reading thumb /Users/fab/code/architectural-render-app/web/protected/config/../../uploads/19/portfolio/thumbs/525cf15532931_IMGP0809.JPG

[25-Oct-2013 10:35:45 Europe/Berlin] ok, reading thumb /Users/fab/code/architectural-render-app/web/protected/config/../../uploads/19/portfolio/thumbs/525439bca4f0d_mercator-bw.png

[25-Oct-2013 10:35:45 Europe/Berlin] ok, reading thumb /Users/fab/code/architectural-render-app/web/protected/config/../../uploads/19/portfolio/thumbs/5252d08d6cbcc_memorialmem_no_trees.jpg

[25-Oct-2013 10:35:45 Europe/Berlin] ok, reading thumb /Users/fab/code/architectural-render-app/web/protected/config/../../uploads/19/portfolio/thumbs/524ed6a2126cc_DSC01374.JPG

[25-Oct-2013 10:35:45 Europe/Berlin] ok, reading thumb /Users/fab/code/architectural-render-app/web/protected/config/../../uploads/19/portfolio/thumbs/524ed69c87327_DSC02912.JPG

[25-Oct-2013 10:35:50 Europe/Berlin] ok, reading thumb /Users/fab/code/architectural-render-app/web/protected/config/../../uploads/19/portfolio/thumbs/524ed6a80ae8e_DSC01579.JPG

[25-Oct-2013 10:35:50 Europe/Berlin] ok, reading thumb /Users/fab/code/architectural-render-app/web/protected/config/../../uploads/19/portfolio/thumbs/524ed6a4bec0b_DSC01876.JPG

[25-Oct-2013 10:35:50 Europe/Berlin] ok, reading thumb /Users/fab/code/architectural-render-app/web/protected/config/../../uploads/19/portfolio/thumbs/524ed6a3b0341_DSC02481.JPG

I’m basically out of ideas… can it be some cache issue ? same issue happens in firefox, and I also tried on a different host, same thing. If I replace the controller action call by the hard coded urls in the view, it works fine… but I’d rather not do that.

Any idea ?

Thanks !

I forgot to say that if I try each image URL individually (that is I open the page /portfolio/thumb/<id>) , it works perfectly for all images…

Don’t know if it really helps, but:

HTTP/0.9 doesn’t know any status codes. This must be Chrome Tools’ interpretation of an entirely empty response.

That can be, the response is indeed empty in the network tab. I get this code by right-click on the request in the network tab and “copy response header”… also in Firefox it’s a bit different : if I empty the cache or reload with ctrl-shift-R, I get all the images, then on subsequent display of the page (via ctrl-r or simply navigate back to the portfolio page), then some images are missing. In Firebug I get no response headers for these images.

I somehow sense a memory issue. But that would usually result into a 500 …

I changed the log level in apache to debug, checked all my logs, no signs of memory issues :(

I’ve also set my php memory_limit to 256M. Same issue…

hmm…

a bit strange but I’ve found that if I add 1 to the content-length, I get better result :

i.e I replace :


header("Content-Length: " . $model->thumb->filesize);

with :


header("Content-Length: " . $model->thumb->filesize + 1 );

(filesize is set using filesize($model->thumb->filepath))

With this only one image fails, sometimes I get all of them… getting better, but feels really hacky :( (I tries +10, no better result…)

Same result if I suppress the header("Content-Length").

That’s really odd. Could you c&p the headers of a working response? Also: What is your setup? Apache+mod_php?

Maybe something, for every failing image I get this message in the chrome developer tool console :


Resource interpreted as Image but transferred with MIME type text/html: "http://ar-app.localhost/portfolio/thumb/378".

I added a log to the controller thumb action, and it looks ok from the PHP side :


      

      error_log('reading thumb for image '.$model->id.', mime is '.$model->thumb->mime);

      $fp = fopen($model->thumb->filepath, 'r');

      header("Content-Type: ". $model->thumb->mime);

      header("Content-length: " . $model->thumb->filesize + 1 );

      fpassthru($fp);

      fclose($fp);



Gives this :


[25-Oct-2013 12:10:22 Europe/Berlin] reading thumb for image 378, mime is image/jpeg

Sure, thanks for your help ;)

Here a working response :


HTTP/1.1 200 OK

Date: Fri, 25 Oct 2013 10:10:22 GMT

Server: Apache/2.2.23 (Unix) mod_ssl/2.2.23 OpenSSL/0.9.8y DAV/2 PHP/5.4.10

X-Powered-By: PHP/5.4.10

Expires: Thu, 19 Nov 1981 08:52:00 GMT

Cache-Control: no-store, no-cache, must-revalidate, post-check=0, pre-check=0

Pragma: no-cache

Keep-Alive: timeout=5, max=97

Connection: Keep-Alive

Transfer-Encoding: chunked

Content-Type: image/jpeg

And yes, apache and mod_php5 (MAMP on OSX)

Hm, that’s without sending a Content-Length, is it? Anyways, the headers are looking ok, so it’s not a caching issue on HTTP level.

I’m a bit puzzled by the “Resource interpreted as Image but transferred with MIME type text/html”-thing. Is it possible some things are falling through your action’s logic? That should result into an empty response.

that’s with content-Length + 1 :


HTTP/1.1 200 OK

Date: Fri, 25 Oct 2013 10:23:56 GMT

Server: Apache/2.2.23 (Unix) mod_ssl/2.2.23 OpenSSL/0.9.8y DAV/2 PHP/5.4.10

X-Powered-By: PHP/5.4.10

Expires: Thu, 19 Nov 1981 08:52:00 GMT

Cache-Control: no-store, no-cache, must-revalidate, post-check=0, pre-check=0

Pragma: no-cache

Content-Length: 33545

Keep-Alive: timeout=5, max=100

Connection: Keep-Alive

Content-Type: image/jpeg

This action is pretty basic :


/**

   * Displays the thumb of an image

   * @param integer $id the ID of the image from which the thumb should be displayed.

   */

  public function actionThumb($id)

  {

    $model=$this->loadImage($id);

    if($model->thumb){

      if(!is_file($model->thumb->filepath)) {

        error_log('not found');

    }


    if(!is_readable($model->thumb->filepath)) {

      error_log('not readable');

    }

      error_log('reading thumb for image '.$model->id.', mime is '.$model->thumb->mime);

      $fp = fopen($model->thumb->filepath, 'r');

      header("Content-Type: ". $model->thumb->mime);

      header("Content-Length: " . ($model->thumb->filesize + 1));

      fpassthru($fp);

      fclose($fp);

      //ob_clean();

      //flush();

      

     // echo file_get_contents($model->thumb->filepath);

    } else {

      // return full size image

      $this->redirect(array('portfolio/image', 'id'=>$id));

    }

  }

I think you would do yourself and everyone else a big favour if you’d throw appropriate CHttpExceptions right after the first two error_logs.

well… I went for lunch and when I came back my computer was…almost dying. After a reboot it seems to be working fine, all images displayed, every time. Hmmmm I hate this >:(

I’ll keep on working on this project this afternoon, let’s see… thanks Da:Sourcerer for your help.

indeed, changed to that :


 

  /**

   * Displays the thumb of an image

   * @param integer $id the ID of the image from which the thumb should be displayed.

   */

  public function actionThumb($id)

  {

    $model=$this->loadImage($id);

    if($model->thumb){

      if(!is_file($model->thumb->filepath)) {

        throw new CHttpException(404);

      }


      if(!is_readable($model->thumb->filepath)) {

        throw new CHttpException(403);

      }

      $fp = fopen($model->thumb->filepath, 'r');

      header("Content-Type: ". $model->thumb->mime);

      header("Content-Length: " . $model->thumb->filesize);

      fpassthru($fp);

    } else {

      // return full size image

      $this->redirect(array('portfolio/image', 'id'=>$id));

    }

  }