Sometimes when a user requests to download a file from your app you may want to run some pre-download logic. For example, you may want to check that the user is authenticated or perhaps log the download. Since CakePHP 2.3 this can be simply achieved using CakeResponse.
Let's say we've got an Asset model that contains details of the files stored in our app. A record contains both the filename
and filepath
. We want to retrieve a file record that has been requested by a user:-
$data = $this->Asset->findById($id);
We then use CakeResponse::file()
to send the file as a response:-
$this->response->file($data['Asset']['filepath']);
We could use CakeResponse::download()
to force the file to download rather than show in the browser, but it's simpler to just instruct CakeResponse::file()
to do this:-
$this->response->file(
$data['Asset']['filepath'],
array(
'download' => true,
'name' => $data['Asset']['filename']
)
);
We've passed a name
parameter to define the filename for the downloaded file.
Finally we return the response object to prevent Cake from rendering a View:-
return $this->response;
Let's put this all together:-
public function download($id) {
// Retrieve the file ready for download
$data = $this->Asset->findById($id);
if (empty($data)) {
throw new NotFoundException();
}
// Run any pre-download logic here.
// Send file as response
$this->response->file(
$data['Asset']['filepath'],
array(
'download' => true,
'name' => $data['Asset']['filename']
)
);
return $this->response;
}
Then all we need to do is create links to this download action rather than directly link to the file in the app. As the file is being download via a Controller action we can use the Auth component to restrict access if that's required; for extra protection the files can be stored outside of the webroot.
We could also place any pre-download logic inside the action before responding with the file such as logging the download.
It's as simple as that!