<p>I have found a solution. </p>

<pre><code>public function actionDownloadAsset()
{
  $this-&gt;requirePostRequest();
  $id = craft()-&gt;request-&gt;getRequiredPost("id");
  $attributes = array('id' =&gt; $id);
  $element = craft()-&gt;elements-&gt;getCriteria(ElementType::Asset)-&gt;first($attributes);


  $file = $entry-&gt;url;
  $name = $entry-&gt;filename;

  header('Content-Description: File Transfer');
  header('Content-Type: application/octet-stream');
  header('Content-Disposition: attachment; filename='.basename($name));
  header('Content-Transfer-Encoding: binary');
  header('Expires: 0');
  header('Cache-Control: must-revalidate');
  header('Pragma: public');

  readfile($file);
}
</code></pre>

<p>To get access to the file url I had to array_shift the $element returned from getCriteria.  Then I had to exclude the file size header. This was ultimately causing the "this webpage is not available". Not sure the reason. </p>

<p><code>header('Content-Length: ' . filesize($entry-&gt;size))</code></p>

CC BY-SA 3.0
2014-08-12T03:34:01.750Z
My only concern is, could I run into memory issues for the file being downloaded?
a-am
CC BY-SA 3.0
2014-08-12T16:47:14.310Z
Oh, right. You won't need the array_shift, if you change it to $element = craft()->elements->getCriteria(ElementType::Asset)->first($attributes); Not sure what's going on with the header size.
Brad Bell
CC BY-SA 3.0
2014-08-12T03:32:46.713Z
2014-08-14T04:16:17.670Z
2014-08-14T04:16:17.670Z
a-am
<p>If you're getting a Craft 404, then it sounds like it's not even making it to your controller's action.</p>

<p>Likely, you haven't <a href="http://buildwithcraft.com/docs/plugins/controllers#allowing-anonymous-access-to-actions" rel="nofollow">allowed anonymous access</a> to your controller globally or that action in particular.</p>

CC BY-SA 3.0
2014-08-11T23:01:07.903Z
I added "allowed anonymous access" to the controller.  If I add a Dump and Exit it gets as far as the element. It doesn't give a Craft 404 just "This webpage is not available".
a-am
CC BY-SA 3.0
2014-08-11T23:05:07.040Z
Check either your Apache error logs or craft/storage/runtime/logs/phperrors.log and see if any fatal errors are getting logged.
Brad Bell
CC BY-SA 3.0
2014-08-12T02:44:08.427Z
No errors in either logs. In my code above am I getting the specific asset properly?
a-am
CC BY-SA 3.0
2014-08-12T03:25:57.520Z
Yeah, that should work fine. Check the response in your browser's console?
Brad Bell
CC BY-SA 3.0
2014-08-11T21:49:02.700Z
2014-08-11T22:14:58.313Z
2014-08-11T22:14:58.313Z
Brad Bell
<p>I have created a plugin that takes a form post, retrieves an asset id and am trying to have the browser start the download process. Is this possible if using a third party source like AmazonS3?  If so what may be wrong with this plugin controller method?</p>

<pre><code>public function actionDownloadAsset()
{
    $this-&gt;requirePostRequest();
    $id = craft()-&gt;request-&gt;getRequiredPost("id");

    $attributes = array('id' =&gt; $id);
    $element = craft()-&gt;elements-&gt;getCriteria(ElementType::Asset)-&gt;limit(1)-&gt;find($attributes);

    header('Content-Description: File Transfer');
    header('Content-Type: application/octet-stream');
    header('Content-Disposition: attachment; filename='.basename($element-&gt;filename));
    header('Content-Transfer-Encoding: binary');
    header('Expires: 0');
    header('Cache-Control: must-revalidate');
    header('Pragma: public');
    header('Content-Length: ' . $element-&gt;filesize);
    readfile($element-&gt;url);
}
</code></pre>

<p>Everytime I try the plugin from front end I get a page not found.  I have made sure the from is posting to the controller method. </p>

<plugin-development><assets>
Can a plugin force download an asset from third-party source like Amazon S3?
# Can a plugin force download an asset from third-party source like Amazon S3?

(Asked by: username_0 on 2014-08-11)

I have created a plugin that takes a form post, retrieves an asset id and am trying to have the browser start the download process. Is this possible if using a third party source like AmazonS3?  If so what may be wrong with this plugin controller method?
public function actionDownloadAsset()
{
    $this->requirePostRequest();
    $id = craft()->request->getRequiredPost("id");

    $attributes = array('id' => $id);
    $element = craft()->elements->getCriteria(ElementType::Asset)->limit(1)->find($attributes);

    header('Content-Description: File Transfer');
    header('Content-Type: application/octet-stream');
    header('Content-Disposition: attachment; filename='.basename($element->filename));
    header('Content-Transfer-Encoding: binary');
    header('Expires: 0');
    header('Cache-Control: must-revalidate');
    header('Pragma: public');
    header('Content-Length: ' . $element->filesize);
    readfile($element->url);
}

Everytime I try the plugin from front end I get a page not found.  I have made sure the from is posting to the controller method.

**Tags:** plugin-development, assets


---

## 2 answers

### Answer by username_0 on 2014-08-12. Score: 3 | Accepted answer

I have found a solution. 
public function actionDownloadAsset()
{
  $this->requirePostRequest();
  $id = craft()->request->getRequiredPost("id");
  $attributes = array('id' => $id);
  $element = craft()->elements->getCriteria(ElementType::Asset)->first($attributes);


  $file = $entry->url;
  $name = $entry->filename;

  header('Content-Description: File Transfer');
  header('Content-Type: application/octet-stream');
  header('Content-Disposition: attachment; filename='.basename($name));
  header('Content-Transfer-Encoding: binary');
  header('Expires: 0');
  header('Cache-Control: must-revalidate');
  header('Pragma: public');

  readfile($file);
}

To get access to the file url I had to array_shift the $element returned from getCriteria.  Then I had to exclude the file size header. This was ultimately causing the "this webpage is not available". Not sure the reason. 
header('Content-Length: ' . filesize($entry->size))

> **Comments:**
> **username_0**: My only concern is, could I run into memory issues for the file being downloaded?  
> **username_1**: Oh, right. You won't need the array_shift, if you change it to $element = craft()->elements->getCriteria(ElementType::Asset)->first($attributes); Not sure what's going on with the header size.  

---

### Answer by username_1 on 2014-08-11. Score: 2

If you're getting a Craft 404, then it sounds like it's not even making it to your controller's action.

Likely, you haven't [allowed anonymous access](http://buildwithcraft.com/docs/plugins/controllers#allowing-anonymous-access-to-actions) to your controller globally or that action in particular.

> **Comments:**
> **username_0**: I added "allowed anonymous access" to the controller.  If I add a Dump and Exit it gets as far as the element. It doesn't give a Craft 404 just "This webpage is not available".  
> **username_1**: Check either your Apache error logs or craft/storage/runtime/logs/phperrors.log and see if any fatal errors are getting logged.  
> **username_0**: No errors in either logs. In my code above am I getting the specific asset properly?  
> **username_1**: Yeah, that should work fine. Check the response in your browser's console?  

---

