Asset manager for images stored in DB

Hi,

I’m trying to take images stored in mysql and ‘cache’ them with the asset manager. I’m following what can be seen in

http://www.yiiframework.com/wiki/148/understanding-assets/

with the difference that I’m publishing images one by one as they are requested, rather than all of them at once (as I don’t have them in a folder unless I first load them from mysql). Here’s a piece of my code:


	public function getImagePath()

	{

		// Read file from DB

		$filename = sys_get_temp_dir().'/image_'.$this->id;

		$handle = fopen($filename, "w");

		fwrite($handle, $this->data);

		fclose($handle);


		// Publish in assets

		$assetPath = Yii::app()->getAssetManager()->publish($filename);

		unlink($filename);

		return $filename." - ".$assetPath;

	}

Note: This function is missing the detection of existing paths to avoid re-publishing. That’s exactly my problem - it doesn’t use the same path twice…

My problem is that each time the page is loaded it creates a new folder under assets rather than using the previous one.

I should probably mention that I haven’t created a component but am calling the publish in my Pictures.php Model file.

Here are my questions:

  • How does Yii ‘know’ that the assets of one component are assigned to a particular assets folder ? In the wiki article it uses $_assetsUrl - where/how is that saved ?

  • Is it possible to publish files one by one rather than an entire folder at the time ? The latter wouldn’t work in my case…

  • Is what I’m trying to do possible using assets ? Should it be done using assets ?

  • Am I better off doing it the ‘old way’ of saving the files in the /images folder ?

Thanks for your inputs :)

Asset manager has not been designed for that, but there’s an easy solution: use a controller to publish images.

When a requested image file cannot be found in the “cache” directory, your web server’s url rewriting mechanism kicks in and initializes your webapp. Now, the image URL, like any other URL, can be routed to a controller action. This action can fetch the image from the database, send it to the client and save it to the “cache” directory.

Hi phtamas,

Thanks for the reply and sorry for not getting back earlier. I’m not sure I understand fully what you’re saying…

Are you saying that I should:

[list=1]

[*]manually create a cache directory which the web server can access directly without calling the webapp ?

[*]make a url rewrite which choses the cached image version and if not found starts the webapp

[*]the webapp controller creates the image in the cache directory for further use

[/list]

If I understood your correctly, I’m not sure how to do (2) :unsure:

  1. Yes, simply an /images directory in your webroot.

  2. You probably already use url rwrite to hide index.php. It will work as is, no modifications required. All you have to do is add an URL rule for missing images that routes to ImageConroller::actionPublish() (or similar)




'images/<filename>' => 'image/publish'



  1. Yes, and don’t forget to also send the image to the client with CHttpRequest::sendFile()

Now you can display images on your pages as




<img src="<?= $this->createUrl('/image/publish', ['filename' => $model->imageFilename]) ?>">



and it will genarate something like




<img src="/images/image.jpg">



Hi again Phtamas,

Thanks for explaining in more detail.

If I understand you correctly, the publish method would be something like

[list=1]

[*]check if file exists in image directory

[*]load file from DB if it doesn’t

[*]send file

[/list]

I think I had misunderstood you earlier as I thought there might be be a way to access /images/<somefile> directly (“apache link”) without initialising Yii/PHP. But I guess that’s not possible … ?

That’s the whole point of it: if /images/imagefilename already exists in the webroot the webserver can simply send it as a static file. If it doesn’t, the server runs index.php and Yii routes the “/images/imagefilename” path to ImageController::actionPublish().

Sorry if my explanation wasn’t clear, english (as probably obvious) isn’t my native language.

Hi again phtamas,

I’m sorry - there’s something I’m not getting…

Here’s what I think I understand:

  • while generating the ‘master’ page yii creates the URL to the image:

<img src="<?= $this->createUrl('/image/publish', ['filename' => $model->imageFilename]) ?>">

  • which is sent to the browser as then turned into something like

<img src="/image/publish?filename=sunset.jpg">

  • this will make the browser connect to the image controller, publish action

  • the publish action can check if the file exists in the /images folder and pass it through if applicable

Here’s the part I don’t understand: How does the browser / server know to load / send file /images/imagefilename instead of /image/publish?filename=sunset.jpg ?

URL rules apply to url generation process too, so the rule




'images/<filename>' => 'image/publish',



will generate an URL like /images/sunset.jpg which works both as a static file URL (when file exists) and a controller/action URL (when file doesn’t exist). Just try it :)

I didn’t know that … interesting. Thanks for clarifying :)

This is awesome. Just tested it successfully and can now do exactly what I wanted :) Thanks for the suggestion !

In summary: what I learnt here:

  • URL routes apply in reverse

  • URL routes apply only if the file doesn’t exist. Otherwise it defaults to the existing file. Great for caching !

This amazing I had just tested it and its working good.