我尝试使用ng-model的输入标签类型文件:

<input type="file" ng-model="vm.uploadme" />

但是在选择了一个文件后,在控制器中,$scope.vm。Uploadme仍然没有定义。

我如何在我的控制器中获得所选文件?


我用指令创建了一个变通方法:

.directive("fileread", [function () {
    return {
        scope: {
            fileread: "="
        },
        link: function (scope, element, attributes) {
            element.bind("change", function (changeEvent) {
                var reader = new FileReader();
                reader.onload = function (loadEvent) {
                    scope.$apply(function () {
                        scope.fileread = loadEvent.target.result;
                    });
                }
                reader.readAsDataURL(changeEvent.target.files[0]);
            });
        }
    }
}]);

input标签变成:

<input type="file" fileread="vm.uploadme" />

或者如果只需要文件定义:

.directive("fileread", [function () {
    return {
        scope: {
            fileread: "="
        },
        link: function (scope, element, attributes) {
            element.bind("change", function (changeEvent) {
                scope.$apply(function () {
                    scope.fileread = changeEvent.target.files[0];
                    // or all selected files:
                    // scope.fileread = changeEvent.target.files;
                });
            });
        }
    }
}]);

这是@endy-tjahjono解决方案的补充。

我最终无法从作用域获得uploadme的值。即使在HTML中的uploadme被指令明显地更新了,我仍然不能通过$scope.uploadme访问它的值。不过,我可以从作用域设置它的值。神秘的,对吧?

结果是,该指令创建了一个子作用域,并且子作用域有自己的uploadme。

解决方案是使用对象而不是原语来保存uploadme的值。

在控制器中我有:

$scope.uploadme = {};
$scope.uploadme.src = "";

在HTML中:

 <input type="file" fileread="uploadme.src"/>
 <input type="text" ng-model="uploadme.src"/>

指令没有变化。

现在,一切都像预期的那样。我可以获取uploadme的值。SRC从我的控制器使用$scope.uploadme。


我使用这个指令:

angular.module('appFilereader', []).directive('appFilereader', function($q) {
    var slice = Array.prototype.slice;

    return {
        restrict: 'A',
        require: '?ngModel',
        link: function(scope, element, attrs, ngModel) {
                if (!ngModel) return;

                ngModel.$render = function() {};

                element.bind('change', function(e) {
                    var element = e.target;

                    $q.all(slice.call(element.files, 0).map(readFile))
                        .then(function(values) {
                            if (element.multiple) ngModel.$setViewValue(values);
                            else ngModel.$setViewValue(values.length ? values[0] : null);
                        });

                    function readFile(file) {
                        var deferred = $q.defer();

                        var reader = new FileReader();
                        reader.onload = function(e) {
                            deferred.resolve(e.target.result);
                        };
                        reader.onerror = function(e) {
                            deferred.reject(e);
                        };
                        reader.readAsDataURL(file);

                        return deferred.promise;
                    }

                }); //change

            } //link
    }; //return
});

然后像这样调用它:

<input type="file" ng-model="editItem._attachments_uri.image" accept="image/*" app-filereader />

属性(editItem.editItem._attachments_uri.image)将使用您选择的文件内容填充为data-uri(!)。

请注意,这个脚本不会上传任何东西。它只会用数据uri (base64)编码的文件内容填充模型。

看看这里的工作演示: http://plnkr.co/CMiHKv2BEidM9SShm9Vv


我必须在多个输入上做同样的事情,所以我更新了@Endy Tjahjono方法。 它返回一个包含所有已读文件的数组。

  .directive("fileread", function () {
    return {
      scope: {
        fileread: "="
      },
      link: function (scope, element, attributes) {
        element.bind("change", function (changeEvent) {
          var readers = [] ,
              files = changeEvent.target.files ,
              datas = [] ;
          for ( var i = 0 ; i < files.length ; i++ ) {
            readers[ i ] = new FileReader();
            readers[ i ].onload = function (loadEvent) {
              datas.push( loadEvent.target.result );
              if ( datas.length === files.length ){
                scope.$apply(function () {
                  scope.fileread = datas;
                });
              }
            }
            readers[ i ].readAsDataURL( files[i] );
          }
        });

      }
    }
  });

这是一个稍微修改过的版本,允许你在作用域中指定属性的名称,就像你在ng-model, usage中所做的一样:

    <myUpload key="file"></myUpload>

指令:

.directive('myUpload', function() {
    return {
        link: function postLink(scope, element, attrs) {
            element.find("input").bind("change", function(changeEvent) {                        
                var reader = new FileReader();
                reader.onload = function(loadEvent) {
                    scope.$apply(function() {
                        scope[attrs.key] = loadEvent.target.result;                                
                    });
                }
                if (typeof(changeEvent.target.files[0]) === 'object') {
                    reader.readAsDataURL(changeEvent.target.files[0]);
                };
            });

        },
        controller: 'FileUploadCtrl',
        template:
                '<span class="btn btn-success fileinput-button">' +
                '<i class="glyphicon glyphicon-plus"></i>' +
                '<span>Replace Image</span>' +
                '<input type="file" accept="image/*" name="files[]" multiple="">' +
                '</span>',
        restrict: 'E'

    };
});

function filesModelDirective(){
  return {
    controller: function($parse, $element, $attrs, $scope){
      var exp = $parse($attrs.filesModel);
      $element.on('change', function(){
        exp.assign($scope, this.files[0]);
        $scope.$apply();
      });
    }
  };
}
app.directive('filesModel', filesModelDirective);

对于使用lodash或下划线输入的多个文件:

.directive("fileread", [function () {
    return {
        scope: {
            fileread: "="
        },
        link: function (scope, element, attributes) {
            element.bind("change", function (changeEvent) {
                return _.map(changeEvent.target.files, function(file){
                  scope.fileread = [];
                  var reader = new FileReader();
                  reader.onload = function (loadEvent) {
                      scope.$apply(function () {
                          scope.fileread.push(loadEvent.target.result);
                      });
                  }
                  reader.readAsDataURL(file);
                });
            });
        }
    }
}]);

我创建了一个指令并在bower上注册。

这个库将帮助您建模输入文件,不仅返回文件数据,而且还返回文件dataurl或base64。

{
    "lastModified": 1438583972000,
    "lastModifiedDate": "2015-08-03T06:39:32.000Z",
    "name": "gitignore_global.txt",
    "size": 236,
    "type": "text/plain",
    "data": "data:text/plain;base64,DQojaWdub3JlIHRodW1ibmFpbHMgY3JlYXRlZCBieSB3aW5kb3dz…xoDQoqLmJhaw0KKi5jYWNoZQ0KKi5pbGsNCioubG9nDQoqLmRsbA0KKi5saWINCiouc2JyDQo="
}

https://github.com/mistralworks/ng-file-model/


如何使<input type="file">与ng-model一起工作

使用ng-model的指令的工作演示

核心ng-model指令不能使用<input type="file">开箱即用。

这个自定义指令启用了ng-model,并具有启用ng-change、ng-required和ng-form指令的额外好处,可以使用<input type="file">。

angular.module("app",[]); angular.module("app").directive("selectNgFiles", 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-ng-files 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>


我必须修改Endy的指令,这样我就可以得到最后修改,lastModifiedDate,名称,大小,类型和数据,以及能够得到一个文件数组。对于那些需要这些额外功能的人,这里就是。

更新: 我发现了一个错误,如果你选择了文件,然后再次选择但取消,这些文件永远不会像它出现时那样取消选择。所以我更新了代码来解决这个问题。

 .directive("fileread", function () {
        return {
            scope: {
                fileread: "="
            },
            link: function (scope, element, attributes) {
                element.bind("change", function (changeEvent) {
                    var readers = [] ,
                        files = changeEvent.target.files ,
                        datas = [] ;
                    if(!files.length){
                        scope.$apply(function () {
                            scope.fileread = [];
                        });
                        return;
                    }
                    for ( var i = 0 ; i < files.length ; i++ ) {
                        readers[ i ] = new FileReader();
                        readers[ i ].index = i;
                        readers[ i ].onload = function (loadEvent) {
                            var index = loadEvent.target.index;
                            datas.push({
                                lastModified: files[index].lastModified,
                                lastModifiedDate: files[index].lastModifiedDate,
                                name: files[index].name,
                                size: files[index].size,
                                type: files[index].type,
                                data: loadEvent.target.result
                            });
                            if ( datas.length === files.length ){
                                scope.$apply(function () {
                                    scope.fileread = datas;
                                });
                            }
                        };
                        readers[ i ].readAsDataURL( files[i] );
                    }
                });

            }
        }
    });

试试这个,这在angular JS中对我有用

    let fileToUpload = `${documentLocation}/${documentType}.pdf`;
    let absoluteFilePath = path.resolve(__dirname, fileToUpload);
    console.log(`Uploading document ${absoluteFilePath}`);
    element.all(by.css("input[type='file']")).sendKeys(absoluteFilePath);

如果你想要一些更优雅/集成的东西,你可以使用一个装饰器来扩展输入指令,支持type=file。要记住的主要警告是这个方法在IE9中不起作用,因为IE9没有实现File API。在IE9或更早的版本中,使用JavaScript通过XHR上传二进制数据(无论哪种类型)是根本不可能的(使用ActiveXObject访问本地文件系统不被视为使用ActiveX只会带来安全问题)。

这个方法也需要AngularJS 1.4。但是你可以使用$ provider .decorator而不是angular.Module.decorator——我写这个要点是为了演示如何在符合John Papa的AngularJS风格指南的情况下做到这一点:

(function() {
    'use strict';

    /**
    * @ngdoc input
    * @name input[file]
    *
    * @description
    * Adds very basic support for ngModel to `input[type=file]` fields.
    *
    * Requires AngularJS 1.4.x or later. Does not support Internet Explorer 9 - the browser's
    * implementation of `HTMLInputElement` must have a `files` property for file inputs.
    *
    * @param {string} ngModel
    *  Assignable AngularJS expression to data-bind to. The data-bound object will be an instance
    *  of {@link https://developer.mozilla.org/en-US/docs/Web/API/FileList `FileList`}.
    * @param {string=} name Property name of the form under which the control is published.
    * @param {string=} ngChange
    *  AngularJS expression to be executed when input changes due to user interaction with the
    *  input element.
    */
    angular
        .module('yourModuleNameHere')
        .decorator('inputDirective', myInputFileDecorator);

    myInputFileDecorator.$inject = ['$delegate', '$browser', '$sniffer', '$filter', '$parse'];
    function myInputFileDecorator($delegate, $browser, $sniffer, $filter, $parse) {
        var inputDirective = $delegate[0],
            preLink = inputDirective.link.pre;

        inputDirective.link.pre = function (scope, element, attr, ctrl) {
            if (ctrl[0]) {
                if (angular.lowercase(attr.type) === 'file') {
                    fileInputType(
                        scope, element, attr, ctrl[0], $sniffer, $browser, $filter, $parse);
                } else {
                    preLink.apply(this, arguments);
                }
            }
        };

        return $delegate;
    }

    function fileInputType(scope, element, attr, ctrl, $sniffer, $browser, $filter, $parse) {
        element.on('change', function (ev) {
            if (angular.isDefined(element[0].files)) {
                ctrl.$setViewValue(element[0].files, ev && ev.type);
            }
        })

        ctrl.$isEmpty = function (value) {
            return !value || value.length === 0;
        };
    }
})();

为什么一开始不这么做呢?AngularJS的支持只支持到IE9。如果你不同意这个决定,并认为他们无论如何都应该把它放进去,那就赶紧转向Angular 2+,因为更好的现代支持正是Angular 2存在的原因。

The issue is (as was mentioned before) that without the file api support doing this properly is unfeasible for the core given our baseline being IE9 and polyfilling this stuff is out of the question for core. Additionally trying to handle this input in a way that is not cross-browser compatible only makes it harder for 3rd party solutions, which now have to fight/disable/workaround the core solution. ... I'm going to close this just as we closed #1236. Angular 2 is being build to support modern browsers and with that file support will easily available.


或者你可以获取输入并设置onchange函数:

<input type="file" id="myFileInput" />
document.getElementById("myFileInput").onchange = function (event) {
   console.log(event.target.files);                        
};