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
You can’t perform that action at this time.