multiple-files

How To Upload Multiple Files in Laravel 5.4

File upload is one of the most commonly used features in web-projects. And it seems pretty easy – form, submit, validation, store. But it gets a little more complex if you want to allow your users to upload more than one file with one input – let’s see how it’s done in Laravel.

1. Preparing the database

Let’s have a simple and probably the most often example – product and its photos. To simplify let’s take only necessary field – product will have a title and N photos. So we create models with migrations – with these commands:

Notice: this -m parameter means that migration should be automatically created with the model – more about it here.

File app/Product.php:

Migration of products table:

Now, a little more complicated thing – for products_photos table we have a model app/ProductsPhoto.php with a relationship method:

And, finally, migration for products_photos table:

Want to move faster? Try our tool to generate Laravel adminpanel without a line of code!
Go to QuickAdminPanel.com

2. Structure: Routes, Controllers and Views

We start with a default Laravel project and will create two pages – upload form and result page. Basically, we need two routes and one controller.

File routes/web.php (you can choose different URLs or method names):

Next we generate a controller – there’s an artisan command for that:

And for now we fill the methods with something like this:

Let’s load URL /upload in the browser.

laravel file upload view error

Got this error “View [upload_form] not found.”? GOOD. It means two things:

  • Route and Controller work ok;
  • It’s time to create the missing view

3. View with Upload Form

For our form let’s use default HTML tags without any Form packages – our form will look like this:

laravel multi file upload form


4. Validation Request

Now let’s get back to our Controller and start filling in uploadSubmit() method. Before actually uploading the file, we probably want to validate the form data.

Let’s say that we have a required product name and files are images not bigger than 2 MB. We create a request file for it:

And then open the file app/Http/Requests/UploadRequest.php.

First, we change authorize() method to return true, otherwise we would get “unauthorized action” on submit.

Now, what we’re interested in is rules() method. We need to fill that array with our own rules of validation.

First, we add product name as required, so we have:

Now, we need to fill the rules for photos. If we had only one field photo, it would look like this:

Notice: you can see all the validation rules (and add more) on this page of official documentation.

But we have more than one file, right? So we can fill in the rules() array dynamically! With a loop, something like this:

Next step – we need to apply this Request class to the Controller, so we change the parameter of the method. Also don’t forget to include it in use on top – PhpStorm did it automatically for me.

Finally, let’s show the validation errors, if there are any. Almost copy-paste from the original Laravel documentation:

Now, if I don’t enter product name and upload an image which is too big – here’s what I will see:

laravel multiple file upload validation errors


5. Storing Data and Files

After all this hard work – let’s finally upload the data. We have two things to take care of – database and file storage. Let’s start with the files, and here we need to know about filesystems in Laravel.

There is a file config/filesystems.php where you specify the locations for your file storage. It allows you to easily configure external storage like Amazon S3, but we won’t do it in this tutorial – we will stick with default parameters:

Basically, it means that all your files will be stored in /storage/app folder. Not in /public, so safely and not accessible directly from browser URL. But you can change it here, if needed.

The file upload itself is incredibly simple in Laravel. Our whole Controller method will look like this:

As you can see, the only method for uploading file is $photo->store(‘photos’). Parameter means what sub-folder to use for storage, so in this case it will be /storage/app/photos. Filename will be created dynamically to a random string. Like this:

laravel multiple file upload store

And here’s how the result looks in database:

laravel multiple file upload database 1
Screen Shot 2017-02-23 at 9.58.50 AM

Basically, that’s it! Of course, you can go a step further and add more validation rules, image resize, thumbnails and more functions. But that’s a topic for another article, I can recommend a few packages to work with images:

Finally, if you want to play around with the code listed here, I’ve prepared you the archive to download. Enjoy!

Want more tips and articles like this?
Subscribe to our weekly newsletter - comes out every Thursday!
Join 1200+ Laravel professionals who read the issue weekly.

35 thoughts on “How To Upload Multiple Files in Laravel 5.4

    1. Good question, Peter. For some reason it didn’t work for me when I tried photos.* => XXX. Probably with files it works differently. Maybe you would write the code and I would update the article? Thanks a lot!

      1. This is also good way to validate various number of photos in array , that you get back from input request. It worked for me. Cheers.

        $validate = count($photo);
        $photos = $input[‘photos’];

        $this->validate(request(),[
        ‘photos’ => ‘required|min:’.$validate
        ]);

  1. Thanks for the detailed write up! Just one minor thing I’d argue, I feel that the naming of the second model and table would make a lot more sense as “ProductPhoto” and “product_photos” respectively, instead of using an awkward convention. It really makes no sense to use plural forms for models, as your model basically describes an object (singular), any relation to another model which might be plural is described by a property/method of the model, not the name itself. And in this case, only your product model is “really” aware that there is a 1 to n relation, so it makes even less sense, on top of that, if you’re going plural, it should’ve been ProductPhotos, because you have more photos per product, but again, that should not be conveyed through the name.

    For your own sanity while coding, using names that are semantically and grammatically correct will lead to less confusion and code that reads naturally.

    1. Thank you, Ken, for the detailed opinion. In general, I agree, that is my personal preference to have database tables “***S” and ***S_***S” therefore I’ve used models like this.

      Don’t have time to change the article now, including screenshots and code files, but will use this logic for the future articles.

  2. from the archive
    I modified web.php
    Route::get(‘/’, function () {
    return view(‘welcome’);
    });
    to have the upload form
    I submited the form but error:

    TokenMismatchException in VerifyCsrfToken.php line 68:

    How can I correct this ?

  3. Thank you for the code and help here. But I was in need of code to access the stored pictures for that particular product.
    Say i have 10 products and each product have 5 or more images then i need to show the images when that particular product is clicked.That is at home page we have only the list of product with thumbnails and when that thumbnails is clicked the details of that product should come with the related images, Can you help me with that? Please… 🙂

    1. Hi Tek Raj,
      How exactly can I help you? What code have you tried and it didn’t work? Or you want me to write a full tutorial for this? I think it’s pretty easy to find some related tutorials on this topic.

      1. I tried a lot. I couldn’t find it even in stack overflow. Yours is the first tutorial i found for storing image/files using relationship. Up to uploading, I completed and worked fine I just needed is the accessing part. You could help me either by providing me the technique following your uploading tutorial or you could write a sample code. Either way its fine for me. I would be very grateful to you. I am stuck with this problem since 3 days. And finally i found yours tutorial so 🙂

        1. This is my Route:
          Route::get(‘/upload’, ‘UploadController@uploadForm’);
          Route::post(‘/upload’, ‘UploadController@uploadSubmit’);
          Route::get(‘/upload/{id}’,’UploadController@show’);

          This is my show function inside UploadController:
          public function show($id)
          {
          // $data=Product::find($id);
          $data =ProductsPhoto::with(‘Product’)->find($id);
          return view(‘showpage’,compact(‘data’));
          }

          This is my showpage code:

          {{$data->name}}
          name}}”/>
          {{– images}}” alt=”a” title=”a” id=”wows1_0″/>
          images}}” alt=”a” title=”a” id=”wows1_0″/> –}}

  4. in controller in show function when i use “$data=Product::find($id);” I can access the product name but if “$data =ProductsPhoto::with(‘Product’)->find($id);” is used i cant get name nor image.I think my img src is wrong. what is the img src for the image stored in storage/app/photos in laravel.

  5. if you use just image mime type for validation in your code then this code can be exploited by uploading a crafted image with php extension

    search in google for image mime type exploit

    1. I was gonna write the same, I think this code is insecure as a user can upload a .php file crafted that appears to be an image, and because this code keeps the original extension

  6. iam getting this error

    “FileException in UploadedFile.php line 251:
    The file “Lighthouse.jpg” was not uploaded due to an unknown error. “

  7. This is not succeeding – have removed everything relating to the database, just trying to get the
    ->$photo->store() to work.

    As part of my troubleshooting I;ve inserted a dump($filename); in the foreach ($request->photos as $photo) { part of the Uploadcontroller`.

    I suspect something critical isn’t happening in the store() function, but as puzzled on how to get to it.

    A pointer in the right direction would be appreciated.

    Cheers – Miles Thompson
    Enfield NS, Canada

  8. i select array = [Untitled.png, 1.png, CV_Bui-Huu-Tai.pdf, Map to TMA.jpg]. Why is it not error with CV_Bui-Huu-Tai.pdf?.
    public function rules()
    {
    $nbr = count($this->input(‘file’));
    foreach(range(0, $nbr) as $index) {
    $rules[‘file.’. $index] = ‘image|max:4000’;
    }
    return $rules;
    }

  9. Hi guys, I keep getting this error –
    “message”: “SQLSTATE[HY000]: General error: 1364 Field ‘product_id’ doesn’t have a default value (SQL: insert into products_photos (photo, updated_at, created_at) values (photos/H0ZXbHGy5GYlap2DVeP4xj0GqYwoZEv0Uqfo9VqK.jpeg, 2017-09-12 09:41:23, 2017-09-12 09:41:23))”,

    Here is my upload controller code –

    public function store(UploadRequest $request)
    {
    $product = Product::create($request->all());
    foreach ($request->images as $photo) {
    $imageName = $photo->store(‘photos’);
    ProductsPhoto::create([
    ‘product_id’ => $product->id,
    ‘photo’ => $imageName
    ]);
    }
    return view(‘products.item’, compact(‘product’));
    }

    What could probably be the problem? I would appreciate your answers.

  10. Is this tutorial incomplete? I get the following: local.ERROR: Class ‘App\Http\Controllers\Product’ not found.

    It seems to be stumbling on Product::create because there is no Controller?

Leave a Reply

Your email address will not be published. Required fields are marked *