Howto : AJAX multiple file upload in Laravel (3)

Update! To adapt this code to Laravel 4 please see my newer post here: https://maxoffsky.com/code-blog/uploading-files-in-laravel-4/

In this short blog post I will share with you how I made queued multiple file upload possible with Laravel and a nice jQuery plugin called Dropzone.js.

I needed to give the user the ability to upload multiple images at once and instantly see visual feedback on the upload progress and/or errors that come up during the upload.

Dropzone.js comes with nice CSS and Javascript that make it a breeze to work with HTML 5 file upload API. It provides you with a file input container that users can drag and drop files unto or just click the container to select multiple files from the file system.

From the plugin docs: Dropzone.js works in :

  • Chrome 7+
  • Firefox 4+
  • IE 10+
  • Opera 12+ (Currently disabled for MacOS because their API is buggy)
  • Safari 5+

For all the other browsers, dropzone provides an oldschool file input fallback.

To implement this awesome plugin here is what you need to do :

1) Download Dropzone.js Javascript and CSS and reference to them from your view (or layout):

{{ HTML::style('css/basic.css');}}
{{ HTML::script('js/vendor/dropzone.js') }}

2) In your view have a form element with class dropzone, action being the path to your upload route or controller and some id:

Upload using Dropzone.js

<form action="{{ url('user/upload')}}" class="dropzone" id="my-awesome-dropzone"></form>

At this point if you go to the view where you implemented the dropzone plugin you should see it working. But it should give you errors when you try to upload files because the action where it tries to upload doesn’t exist yet. Now we need to hook up the form to a route or controller.

I have a controller called “user”, and the action that I need to build should do the following things:

  • Validate my file upload to be an image (if I want only images) and below certain size limit in kilobytes.
  • If the validation passes, upload the file and rename it to a random string to avoid collisions.
  • If validation does not pass return the first error to the user and return status code 400.
  • Upon file upload success make a JSON response with code 200 (because 200 is commonly used as a success response)
  • Otherwise throw an error letting the user know that there was a server error.

Now that I have my algorithm figured out, I build the controller action :

public function post_upload(){

		$input = Input::all();
		$rules = array(
		    'file' => 'image|max:3000',
		);

		$validation = Validator::make($input, $rules);

		if ($validation->fails())
		{
			return Response::make($validation->errors->first(), 400);
		}

		$file = Input::file('file');

        $extension = File::extension($file['name']);
        $directory = path('public').'uploads/'.sha1(time());
        $filename = sha1(time().time()).".{$extension}";

        $upload_success = Input::upload('file', $directory, $filename);

        if( $upload_success ) {
        	return Response::json('success', 200);
        } else {
        	return Response::json('error', 400);
        }
	}

Side note, make sure you create a folder called “uploads” in the public folder because other files and folders will be created there.

Now if you have your routes set up as mine (a user controller must be registered in the routes) and this action implemented you should see that the files are uploaded successfully and if you navigate to your public/uploads folder you should see something like this:

That’s it!

If you need the files to be resized/cropped in PHP before saving, make sure you check out some packages on Packagist, specifically :

http://intervention.olivervogel.net/

Let me know if you have any questions/problems with this.

Liked it? Take a second to support Maks Surguy on Patreon!
Become a patron at Patreon!

You may also like

61 comments

  • ben March 6, 2013  

    Thanks for all the Laravel Tutorials. Keep them coming!

  • Burak Erdem March 7, 2013  

    Hi,

    Thanks for this tutorial but your form’s HTML is not displayed correctly. It should be like below;

    And if you enabled CSRF protection, which you must, then you have to insert token in your form;

    {{ Form::token() }}

  • Burak Erdem March 7, 2013  

    WordPress messed my previous comment. The HTML form should be;

    form id=”my-awesome-dropzone” action=”{{ url(‘user/upload’) }}” class=”dropzone”

  • Vaqas March 13, 2013  

    Can you please share a tutorial where we can show the save image with the post, Like you made the blog tutorial if some one want to publish the image with post how we can do that ? I am waiting for your positive response.

    Thanks

    Regards,

  • Robert Taylor March 14, 2013  

    on ln 18 you have $directory = path(‘public’).’uploads/’.sha1(time());

    Does anyone know in Laravel4, how to access path()? It does not appear to be present.

  • Maks Surguy March 15, 2013  

    just try to do ‘public/uploads’ instead of using “path(public).’uploads'”.

  • Vaqas Uddin March 21, 2013  

    If I dont want to create new folder just saving all files in one folder how I can do that?

  • Maks Surguy March 24, 2013  

    Just replace
    path(‘public’).’uploads/’.sha1(time());

    with

    path(‘public’).’uploads/yourfolder’);

    =)

  • Gabriel April 27, 2013  

    Hi, thank you for your tutorial, works perfectly.

    Sometimes i need to upload only one file and prevent multiple choice. I can’t find how i can do that with dropzone.

    Is it possible?

    Thank you one file

  • Maks Surguy April 30, 2013  

    I have searched its documentation and I don’t think you can do that with dropzone.
    As a better alternative for AJAX style uploads I recommend this – http://malsup.com/jquery/form/#file-upload

  • andsien May 3, 2013  

    Hi thanks for this turorial. One thing, iv got problems when image width dimension is above 2031 px. I wont upload and give no error message on the dropzone. The green check mark is shown but wont upload. Thanks, Hope you can help me on this.

  • Maks Surguy May 5, 2013  

    That probably is the PHP upload limit issue. Try increasing the max size of file uploads

  • triezrjas May 8, 2013  

    Hi I’m new to Laravel and your code helped me a lot. My problem after uploading files is to show or view the list of files uploaded. I hope you can help me. 🙂

  • Maks Surguy May 9, 2013  

    Are you using Laravel 3 or 4 ?
    I prefer to save information about the uploaded files into the database and then just showing the items from the database

  • Rudra May 13, 2013  

    How to add Remove per file and remove all files option …
    Thanks in Advance

  • Maks Surguy May 18, 2013  

    You would have to make that as a separate AJAX call to the server. Let’s say your user is logged in, then from the page with file uploads you make a POST request to your application’s API or controller action with the ID of the file, and on the server side you have to make sure that this file indeed belongs to the user and then you can use Laravel’s file removal function to remove the file.

    Does that solve your question?

  • web June 13, 2013  

    no doubt good work.
    Can you give a zipped example… not only files to configure.
    one i can put on server and emediately see it working without making upload folder and the like.

    it will be a 1000 times better than tutorial.
    or please email me such zipped at webuzy at yahoo of com

  • Maks Surguy June 14, 2013  

    I am sorry, I don’t have the complete example ready to be shared in full. The example that I had transformed into a full application : http://fldrp.me

  • RHeno June 14, 2013  

    {“error”:{“type”:”Symfony\Component\HttpKernel\Exception\NotFoundHttpException”,”message”:””,”file”:”C:\xampp-portable\htdocs\jamshop\bootstrap\compiled.php”,”line”:4921}}

    What The Problem?

  • Maks Surguy June 15, 2013  

    try running “composer dump-autoload”
    and “php artisan optimize”
    commands

  • Eugene June 26, 2013  

    Your links to Dropzone website are broken in this post…

  • marco July 5, 2013  

    Hey Marks Im confused with the route, always recibe a 500 internal Server Error

    this is my route
    Route::get(‘/’, function()
    {
    return View::make(‘home.index’);
    });

    Route::post(‘user/upload’, ‘user@upload’);

    …..
    and my controller

    ‘image|max:3000’,
    …..

    what is wrong in the code ?
    can u help me?

  • Maks Surguy July 5, 2013  

    Which version of Laravel are you using ?
    If using Laravel 4 please follow the link I just posted at the top of this post that says “Update …” because in Laravel 4 things are a bit different

  • yusida July 8, 2013  

    Hello there, I want to ask if possible to show image that already uploaded and saved in database to dropzone? I means when we refresh the page, the image that was saved in database is still show in the dropzone area.
    #sory for bad english

  • marco July 9, 2013  

    ohh sorry I dont recibe a mail for you reply… ist both l3 and l4 … its the same error POST http://upload-l4/user/upload 500 (Internal Server Error)

    I confused in the route part …
    this is my route file

    Route::get(‘/’, function()
    {
    return View::make(‘hello’);
    });

    Route::post(‘user/upload’, ‘HomeController@upload’);

  • KMJ August 6, 2013  

    Thanks used this as base for Zend 2 version.

  • Francisco September 17, 2013  

    Sorry, If anybody are implemented this dropzone.js and can help me?.

    I need some changes but Im not professional with Javascript.

    I need upload only ONE photo a time, and when the foto is right upload show button to aks to the cliente ‘Is this the photo?, and press OK then jumping automaticaly to my process page in php to recording the register in the database with Mysql.

    Please help me.

    Francisco (from Spain)
    (excuse me my bad english)

  • pankaj thakur October 7, 2013  

    Hello,
    It,s good work. but if any user upload wrong file then there should be option for remove file. is it possible with this plugin?
    if yes then how???? 😮

  • Maks Surguy October 9, 2013  

    The new version of the plugin allows this, you have to use addRemoveLinks option to be true:

    var myDropzone = new Dropzone("#screenshots-dropzone",{
    url: "{{url('admin/screenshots/upload')}}",
    addRemoveLinks: true,
    maxFiles : 4,
    acceptedFiles: 'image/*',
    maxFilesize: 5
    });

    And then create a handler to send a POST request on item removal:
    myDropzone.on("removedfile", function(file) {
    if(file.xhr){
    var url = JSON.parse(file.xhr.response);
    var imagepath = url.url;
    $.post( "{{url('admin/screenshots/remove')}}",{ url : imagepath });
    }
    myDropzone.options.maxFiles++;
    });

    And of course add the backend routes for the actual process to remove the files…

  • Chris March 11, 2014  

    In laravel 4 its public_path()

  • Shif March 31, 2014  

    HI, I’m totally new to laravel! How should I have the route for this?

  • Maks Surguy March 31, 2014  

    You would place it into a POST route that you define like this:
    Route::post(‘upload’, function(){
    //place code here
    })

  • Sohail Khan April 7, 2014  

    Hi Maks, I want to integrate your excellent code snippet into my app. The problem I am having is that I rename each file to a random file name and want to store some meta information about each file in the db. However, I have no idea how to detect in the dom, which image was clicked so I can configure its meta info in another form (because I’ve renamed it). Can u help me?

  • sandeep May 20, 2014  

    i have the same question… especially when i tried to use dropzonejs on iOS, all the photographs that we select will upload with the same name “image.jpg”, which caused the collisions and only the final image file will be uploaded into the uploads folder. adding the random content to the files while uploading resolved the issue. but it caused an other problem.. i.e. the delete links not working on the images as the images uploaded into the folder are of different names that are there in dropzone.

  • Joseph Rex June 14, 2014  

    One thing I noticed was you had dropped just “dropzonejs.com” in your anchor’s href. This makes it see it as being relative to your website. It should be “http://dropzonejs.com” to reference it properly. Thanks for the tutorial anyway

  • Luka Čavić September 9, 2014  

    Hello, I have a form for ading a new task, and that task has images…I want to integrate field (dropzone) inside current form (add task), so when I click “Save task”, images start to load, after loading…becouse I want to save image info to db for accessing later.

  • maxsurguy September 9, 2014  

    Ok, what exactly is your question?

  • Christopher September 14, 2014  

    How to store the files to the database ? ( e.g the path of the image and an ID )

  • maxsurguy September 14, 2014  

    You would do that by storing the $directory and the $filename in the DB. For example I would have a table called “items” and have columns “path” and “filename” that I’d insert those values into. Hope that helps!

  • Fadi Fannoun September 24, 2014  

    Thanks for your effort

    I’ve got this exception: TokenMismatchException

  • maxsurguy September 24, 2014  

    That means you have a CSRF filter on that route and you need to also pass a CSRF token with the file payload.

  • Luka Čavić October 12, 2014  

    How to do that? 🙂 My email is luka . cavic @ gmail . com – Can I contact you for help with this? 🙂

  • Miro Hristov February 7, 2015  

    Thanks a lot! You rock!

    In laravel 4.2 you can use the form helper:
    {{Form::open(array(‘url’ => ‘users/upload’, ‘method’ => ‘POST’, ‘files’ => ‘true’, ‘data-ajax’ => ‘ture’, ‘class’ => ‘dropzone’))}}
    {{ Form::close() }}

  • Koyix February 26, 2015  

    I’m always get error with this : Class ‘AppHttpControllersFile’ not found

    Please help

  • Kawsara M. March 18, 2015  

    Import it in your document

    use File;

  • Hossein shojaeyan July 16, 2015  

    For laravel 5 ?

  • Lucas Gabriel August 7, 2015  

    noice. but how do i delete the file afterwards? many thx!

  • Rentilon January 31, 2016  

    Hi!

    I have followed the steps and this doesn’t work, my file object on server y totally empty and dunno why.

    These are my lines of code:

    Route:

    Route::post(‘{zip}/{street}/photos’,’FlyersController@addPhoto’);

    Blade template with Dropzone:

    zip }}/{{ $flyer->street }}/photos” class=”dropzone”>
    {{ csrf_field() }}

    My controller

    public function addPhoto(Request $request,$zip,$street)
    {
    $input = Input::all();
    return Response::make($input, 200);
    }

    The problem is that the file object is empty, the response from the server is this:

    {“_token”:”pjWaWbuKzCXE3R8UrOnc9pepaHIKVIajzR1MJYOl”,”file”:{}}

    So file is created but is totally empty!!

    Any ideas? Thanks a lot!

  • angelor tuyo February 6, 2016  

    the post_upload doesn’t work. it returns a bunch of html codes.

  • Pingback: Google Api Laravel | I love You Zones November 22, 2016  
  • RAOUF KESKES April 21, 2017  

    Cannot use object of type IlluminateHttpUploadedFile as array
    how to fix it ?

  • Anirudh Mishra May 20, 2017  

    im getting 401 (Unauthorized) error.

    im also add csrf field . How can i resolve this ? Pleas help

  • Mwenda Mutuma July 18, 2017  

    Thanks for the tutorial
    Could you please create one on laravel 5 whereby each of the images can be uploaded on different columns in the database table

  • maxsurguy July 19, 2017  

    Hello! I think I have a tutorial for Laravel 4 that should work on L5, it is in my book : https://maxoffsky.com/frontend/

Leave a comment