Skip to content

Instantly share code, notes, and snippets.

Embed
What would you like to do?
Craft CMS plugin example for handling AJAX file uploads
<?php
namespace Craft;
class Plugin_FileController extends BaseController
{
protected $valid_extensions = array('jpg', 'jpeg', 'png', 'pdf');
protected $assetSourceId = 1;
public function actionUpload()
{
$this->requireAjaxRequest();
$errors = array();
$success = array();
foreach ($_FILES['files']['error'] as $key => $error)
{
if (!$error)
{
$filename = $_FILES['files']['name'][$key];
$file = $_FILES['files']['tmp_name'][$key];
$extension = end(explode('.', $filename));
if (!in_array($extension, $this->valid_extensions))
{
$errors[] = "$filename has an invalid extension.";
continue;
}
$uploadDir = craft()->assetSources->getSourceById($this->assetSourceId)->settings['path'];
if (move_uploaded_file($file, $uploadDir . $filename))
{
IOHelper::deleteFile($file);
$file = $uploadDir . $filename;
$fileModel = new AssetFileModel();
$fileModel->sourceId = $this->assetSourceId;
$fileModel->folderId = $this->assetFolderId;
$fileModel->filename = IOHelper::getFileName($filename);
$fileModel->kind = IOHelper::getFileKind(IOHelper::getExtension($filename));
$fileModel->size = filesize($file);
$fileModel->dateModified = IOHelper::getLastTimeModified($file);
craft()->assets->storeFile($fileModel);
$success[] = "$filename was saved.";
}
else
{
$errors[] = "$filename was unable to be saved.";
continue;
}
}
}
$this->returnJSON(compact('errors', 'success'));
}
}
<form method="post" action="" accept-charset="UTF-8" enctype="multipart/form-data">
<input type="hidden" name="action" value="plugin/file/upload" />
<input type="hidden" name="redirect" value="" />
<p><input type="file" name="file" multiple></p>
<input type="submit" class="btn submit" value="Upload">
</form>
<div style="margin-top:20px" id="output" />
{% set js %}
$(function() {
$("form").submit(function() {
var url = '/admin/actions/' + $(this).children("[name=action]").attr('value'),
files = $(this).find("[name=file]")[0].files,
formdata = new FormData(),
file,
reader;
for (var i = 0; i < files.length; i++) {
file = files[i];
if (window.FileReader) {
reader = new FileReader();
reader.readAsDataURL(file);
}
formdata.append("files[]", file);
}
$("#output").html("Uploading...");
$.ajax({
type: "POST",
url: url,
data: formdata,
processData: false,
contentType: false,
success: function(json) {
window.json = json;
console.log(json);
var errors = json['errors'],
success = json['success'],
html = "<p><b>" + errors.length + " errors, " + success.length + " successfully uploaded</b></p>";
$("#output").html(html);
},
error: function (xhr, ajaxOptions, thrownError) {
alert("ERROR: " + xhr.status + " " + thrownError);
}
});
return false;
});
})
{% endset %}
{% includeJs js %}
@owldesign

This comment has been minimized.

Copy link

@owldesign owldesign commented Jun 10, 2015

$this->assetFolderId... assetFolderId is not being defined like you have defined assetSourceId

@medoingthings

This comment has been minimized.

Copy link

@medoingthings medoingthings commented Nov 3, 2016

Thanks a lot for this! Great starting point to get things going.

@SyedTousif

This comment has been minimized.

Copy link

@SyedTousif SyedTousif commented Feb 7, 2017

Great..!!!

@MFFunmaker

This comment has been minimized.

Copy link

@MFFunmaker MFFunmaker commented Aug 2, 2017

In the HTML, I ran into one problem:

I had to use square brackets in the name attribute of the file upload input element, like:

<input type="file" name="file[]" multiple>


In the PHP, there are three things that were causing issues for me:

The $_FILES superglobal was not indexed by 'files', but rather 'file', even with multi-uploads:
$_FILES['file']['name'][$key];

Additional, the end() function only accepts an argument passed by reference. It has to be a real variable and cannot be the return of a function. Simply assign the output of explode() to a variable then pass the variable, like this:

$filenamePieces = explode('.', $filename);
$extension = end($filenamePieces);

Lastly, the function to grab the asset base path was returning the literal "{basePath}/path/to/assets" string with the curly braces and all. The placeholder was not replaced. Having set up my config environment variables exactly according to the docs, to fix this, I used this code:

$uploadDir = craft()->assetSources->getSourceById($this->uploadAssetSourceId)->settings['path'];
$uploadDir = str_replace('{basePath}', craft()->config->get('environmentVariables')['basePath'], $uploadDir);

Hopefully, this helps someone down the road!

@hendrikeng

This comment has been minimized.

Copy link

@hendrikeng hendrikeng commented Mar 25, 2018

@MFFunmaker I tried to implement it according to your changes, however i am running into ERROR: 500 Internal Server Error

this is the form (it throws a syntax error if i change the name to file[]):

  <form class="upload" method="POST" enctype="multipart/form-data">
    <input  type="hidden" name="action" value="assetLinks/upload/UploadFile">
    {{ getCsrfInput() }}
    <input class="file btn" type="file" name="file" multiple>
    <button data-icon="upload" class="uploadfile btn submit" type="submit">Upload file</button>
  </form>

this is the controller:

<?php
namespace Craft;

class AssetLinks_UploadController extends BaseController
{

  protected $valid_extensions = array('jpg', 'jpeg', 'png', 'pdf');
  protected $assetSourceId = 1;
  public function actionUploadFile()
  { 
      $this->requireAjaxRequest();
      $errors = array();
      $success = array();
      foreach ($_FILES['files']['error'] as $key => $error)
      {
          if (!$error)
          {
              $filename = $_FILES['files']['name'][$key];
              $file = $_FILES['files']['tmp_name'][$key];
              $filenamePieces = explode('.', $filename);
              $extension = end($filenamePieces);
              if (!in_array($extension, $this->valid_extensions))
              {
                  $errors[] = "$filename has an invalid extension.";
                  continue;
              }

              $uploadDir = craft()->assetSources->getSourceById($this->uploadAssetSourceId)->settings['path'];
              $uploadDir = str_replace('{basePath}', craft()->config->get('environmentVariables')['basePath'], $uploadDir);
              if (move_uploaded_file($file, $uploadDir . $filename))
              {
                  IOHelper::deleteFile($file);
                  $file = $uploadDir . $filename;
                  $fileModel = new AssetFileModel();
                  $fileModel->sourceId = $this->assetSourceId;
                  $fileModel->folderId = $this->assetFolderId;
                  $fileModel->filename = IOHelper::getFileName($filename);
                  $fileModel->kind = IOHelper::getFileKind(IOHelper::getExtension($filename));
                  $fileModel->size = filesize($file);
                  $fileModel->dateModified = IOHelper::getLastTimeModified($file);
                  craft()->assets->storeFile($fileModel);
                  $success[] = "$filename was saved.";
              }
              else
              {
                  $errors[] = "$filename was unable to be saved.";
                  continue;
              }
          }
      }
      $this->returnJSON(compact('errors', 'success'));
  }
}
@yogesh64bit

This comment has been minimized.

Copy link

@yogesh64bit yogesh64bit commented Jul 17, 2018

Hello,

$this->requireAjaxRequest();

This is defined any where or It is Craft by default function. How I can use in Craft 3. Can you please guide me for this?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment