Yii – Rewrite file or image urls for download or display

fetchit-21

I recently had a need to track files and images which were uploaded and for their urls to be secret. This meant that they couldn’t sit in a folder waiting to be found and had to be saved below the site root.

To get access to them I wanted to the check the user permissions followed by forcing download of all files (including images). Here are the steps I took to accomplish this…

For the sake of this page, my (fake) file urls are something like:

example.com/user/123/file/456.pdf

Update the urlmanager to rewrite files to my filemanager/view:

 
	'user/<user_id:\d+>/file/<id:\d+>.[a-z0-9]+' => 'filemanager/view',

Update the filemanager class’ actionView function:

 
	public function actionView($id)
	{
		$file = $this->loadModel($id);
 
		header("Pragma: public"); // required
		header("Expires: 0");
		header("Cache-Control: must-revalidate, post-check=0, pre-check=0");
		header("Cache-Control: private",false); // required for certain browsers 
		//header("Content-Type: " . $file->mime_type);
		header("Content-Disposition: attachment; filename=\"" . str_replace(" ", "-", preg_replace("@[^a-z0-9 ]@", "", strtolower($file->file_name))) . '.' . $file->file_extension . "\";" );
		header("Content-Transfer-Encoding: binary");
		header("Content-Length: ".filesize(Yii::app()->basePath . '/../../uploads/user_files/' . $file->file_id . '.' . $file->file_extension));
		readfile(Yii::app()->basePath . '/../../uploads/user_files/' . $file->file_id . '.' . $file->file_extension);
		exit();
 
	}

I also of course required filemanager access to be strict:

 
	public function accessRules()
	{
		return array(
			array('allow', 
				'actions'=>array('view', 'create','delete'),
				'users'=>array(),
				'expression' => 'User::model()->hasRightsToFile()',
			),
			array('deny',  // deny all users
				'users'=>array('*'),
			),
		);
	}

And that’s about it. Controlled and forced file download with rewritten file urls.

3 comments.

  1. I’m just starting to dive into Yii and am building a site that requires similar functionality for serving files.

    I was wondering if there was some benefit or issue avoided by manually outputting headers & using readfile() rather than using the sendFile (or xSendFile) function
    ( http://www.yiiframework.com/doc/api/1.1/CHttpRequest#sendFile-detail )

    Thank you in advance. Great little repository of tricks you have here.

  2. No problem using the built in functions as far as I can tell, other than I wanted complete control over the headers.

    Try them out and let me know if you get the same results.

  3. Hi Harry, very interesting your solution, please could you leave an example, I am a beginner in Yii. Thanks a lot.