Viewed   101 times

I need to upload image and video files to the server in an Angular application using Laravel 5.1 as the back end. All Ajax requests need to go to the Laravel controller first, and we have the code there for how the file gets handled when it gets there. We have previously done normal HTML forms to submit file uploads to the controller, but in this case we need to avoid the page refresh of a form, so I am attempting this in Ajax through Angular.

What information do I need to send to the Laravel controller with Ajax that was being sent to the controller via an HTML form previously?

This is the code in the Laravel controller that handled the file information once it got there. That's what I need to figure out how to send, so I can hopefully reuse this code:

    $promotion = Promotion::find($id);
    if (Input::hasFile('img_path')){
        $path             = public_path().'/images/promotion/'.$id.'/';
        $file_path        = $path.'promotion.png';
        $delete           = File::delete($file_path);
        $file             = Input::file('img_path');
        $uploadSuccess    = $file->move($path, 'promotion.png');
        $promotion->img_path = '/images/promotion/'.$id.'/promotion.png';
    }
    if (Input::hasFile('video_path')){
        $path             = public_path().'/video/promotion/'.$id.'/';
        $file_path        = $path.'promotion.mp4';
        $delete           = File::delete($file_path);
        $file             = Input::file('video_path');
        $uploadSuccess    = $file->move($path, 'promotion.mp4');
        $promotion->video_path = '/video/promotion/'.$id.'/promotion.mp4';
    }

As you can see above, we are converting whatever file we get to a PNG with the file name promotion.png so it's easy to fetch, and we are only accepting .mp4 video format. Because of that, we don't need to worry about checking if the file exists and is it ok to overwrite it. That's why you can see in the code we delete any existing file of that name before saving.

The HTML was just an input with a type of "file:

<input type="file" id="img_path" name="img_path" class="promo-img-path" accept="image/*">

We are using Angular now so I can't just send the above through an HTML form anymore. That's what I need to figure out how to do.

We are two developers just doing our best, so I'm sure there is a better way of doing this. However before I refactor this whole thing, I'm hoping I can use Angular (or jQuery as a last resort) to just send the controller whatever file data Laravel needs in order to make the above code work. The answer may be as simple as "send a PUT to the method in that controller above, but instead of a normal JSON payload, use file info in this format and you can gather that info with..."

I would also appreciate any tips on better ways I can do this in the future.

 Answers

5

How to POST FormData Using the $http Service

When using the FormData API to POST files and data, it is important to set the Content-Type header to undefined.

var fd = new FormData()
for (var i in $scope.files) {
    fd.append("fileToUpload", $scope.files[i]);
}
var config = {headers: {'Content-Type': undefined}};

var httpPromise = $http.post(url, fd, config);

By default the AngularJS framework uses content type application/json. By setting Content-Type: undefined, the AngularJS framework omits the content type header allowing the XHR API to set the content type. When sending a FormData object, the XHR API sets the content type to multipart/form-data with the proper boundaries and base64 encoding.

For more information, see MDN Web API Reference - XHR Send method


How did you get the file information into $scope.files?

How to enable <input type="file"> to work with ng-model

This directive also enables <input type="file"> to automatically work with the ng-change and ng-form directives.

angular.module("app",[]);

angular.module("app").directive("selectFilesNg", function() {
  return {
    require: "ngModel",
    link: function postLink(scope,elem,attrs,ngModel) {
      elem.on("change", function(e) {
        var files = elem[0].files;
        ngModel.$setViewValue(files);
      })
    }
  }
});
<script src="//unpkg.com/angular/angular.js"></script>
  <body ng-app="app">
    <h1>AngularJS Input `type=file` Demo</h1>
    
    <input type="file" select-files-ng ng-model="fileArray" multiple>

    <code><table ng-show="fileArray.length">
    <tr><td>Name</td><td>Date</td><td>Size</td><td>Type</td><tr>
    <tr ng-repeat="file in fileArray">
      <td>{{file.name}}</td>
      <td>{{file.lastModified | date  : 'MMMdd,yyyy'}}</td>
      <td>{{file.size}}</td>
      <td>{{file.type}}</td>
    </tr>
    </table></code>
    
  </body>

RECOMMENDED: POST Binary Files Directly

Posting binary files with multi-part/form-data is inefficient as the base64 encoding adds an extra 33% overhead. If the server API accepts POSTs with binary data, post the file directly.

See How to POST binary files with AngularJS (with DEMO)

Monday, September 12, 2022
3

This is possible in HTML5. Example (PHP 5.4):

<!doctype html>
<html>
    <head>
        <title>Test</title>
    </head>
    <body>
        <form method="post" enctype="multipart/form-data">
            <input type="file" name="my_file[]" multiple>
            <input type="submit" value="Upload">
        </form>
        <?php
            if (isset($_FILES['my_file'])) {
                $myFile = $_FILES['my_file'];
                $fileCount = count($myFile["name"]);

                for ($i = 0; $i < $fileCount; $i++) {
                    ?>
                        <p>File #<?= $i+1 ?>:</p>
                        <p>
                            Name: <?= $myFile["name"][$i] ?><br>
                            Temporary file: <?= $myFile["tmp_name"][$i] ?><br>
                            Type: <?= $myFile["type"][$i] ?><br>
                            Size: <?= $myFile["size"][$i] ?><br>
                            Error: <?= $myFile["error"][$i] ?><br>
                        </p>
                    <?php
                }
            }
        ?>
    </body>
</html>

Here's what it looks like in Chrome after selecting 2 items in the file dialog:

And here's what it looks like after clicking the "Upload" button.

This is just a sketch of a fully working answer. See PHP Manual: Handling file uploads for more information on proper, secure handling of file uploads in PHP.

Wednesday, August 24, 2022
5

Seems that ngRepeat order the data by the $$hash, probably the workaround is to use a function that list the keys and then iterate over them:

 $scope.keys = function(obj){
  return obj? Object.keys(obj) : [];
 }

http://plnkr.co/edit/S8BiSPy9axQobSZVwk3D?p=preview

Monday, August 1, 2022
 
gregd
 
2

Change the LoadFileData function

$scope.LoadFileData = function (files) {
        $scope.files.push(files[0]);
    };

files returns a Filelist object and actual file object is at index 0.

Wednesday, December 21, 2022
1

Laravel has store function use this

foreach ($request->file as $file) {
  $filename = $file->store('folder');
}

Here folder is storage/app/folder

You need to change in FormData like this, you have to add file[] array in FormData

for (let i = 0; i < obj.file.length; i++) {
     let f = obj.file[i];
     fd.append('file[]', f);
 }
Wednesday, September 28, 2022
 
eima
 
Only authorized users can answer the search term. Please sign in first, or register a free account.
Not the answer you're looking for? Browse other questions tagged :