Answers.Body: <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>
  
Answers.ContentLicense: CC BY-SA 3.0
Answers.CreationDate: 2014-08-12T03:34:01.750Z
Answers.Text: My only concern is, could I run into memory issues for the file being downloaded?
Answers.UserDisplayName: a-am
Answers.ContentLicense: CC BY-SA 3.0
Answers.CreationDate: 2014-08-12T16:47:14.310Z
Answers.Text: 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.
Answers.UserDisplayName: Brad Bell
Answers.ContentLicense: CC BY-SA 3.0
Answers.CreationDate: 2014-08-12T03:32:46.713Z
Answers.LastActivityDate: 2014-08-14T04:16:17.670Z
Answers.LastEditDate: 2014-08-14T04:16:17.670Z
Answers.UserDisplayName: a-am
Answers.Body: <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>
  
Answers.ContentLicense: CC BY-SA 3.0
Answers.CreationDate: 2014-08-11T23:01:07.903Z
Answers.Text: 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".
Answers.UserDisplayName: a-am
Answers.ContentLicense: CC BY-SA 3.0
Answers.CreationDate: 2014-08-11T23:05:07.040Z
Answers.Text: Check either your Apache error logs or craft/storage/runtime/logs/phperrors.log and see if any fatal errors are getting logged.
Answers.UserDisplayName: Brad Bell
Answers.ContentLicense: CC BY-SA 3.0
Answers.CreationDate: 2014-08-12T02:44:08.427Z
Answers.Text: No errors in either logs. In my code above am I getting the specific asset properly?
Answers.UserDisplayName: a-am
Answers.ContentLicense: CC BY-SA 3.0
Answers.CreationDate: 2014-08-12T03:25:57.520Z
Answers.Text: Yeah, that should work fine. Check the response in your browser's console?
Answers.UserDisplayName: Brad Bell
Answers.ContentLicense: CC BY-SA 3.0
Answers.CreationDate: 2014-08-11T21:49:02.700Z
Answers.LastActivityDate: 2014-08-11T22:14:58.313Z
Answers.LastEditDate: 2014-08-11T22:14:58.313Z
Answers.UserDisplayName: Brad Bell
Body: <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>
  
Tags: <plugin-development><assets>
Title: Can a plugin force download an asset from third-party source like Amazon S3?
ThreadText: # 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?  
  
  ---
  
  