Merida Design Blog

Publicado el | Tutoriales / ,

Subir archivos con AngularJS

Crear aplicaciones web con AngularJS es realmente sencillo, puedes tener todo el CRUD definiendo solamente un $resource conectado a un servicio REST.

Sin embargo, algo que el $resource de AngularJS no nos permite, es enviar archivos al servicio REST, al menos no sin realizar algunos ajustes, veamos el código.


var myApp = angular.module('myApp', ['ngResource']);
myApp.config(['$httpProvider', function($httpProvider) {

  $httpProvider.defaults.transformRequest = function(data) {
    if(undefined === data) return data;
    var formData = new FormData();
    angular.forEach(data, function(value, key) {
      if(value instanceof FileList) {
        if(value.length === 1)
          formData.append(key, value[0]);
        else {
          angular.foreach(value, function(file, index) {
            formData.append(key + '_' + index, file);
          });
        }
      } else {
        formData.append(key, value);
      }
    });
    return formData;
  };
  $httpProvider.defaults.headers.post['Content-Type'] = undefined;

}]);

Fuente original

Lo que estamos haciendo es, modificar comportamiento estándar del $httpProvider, convirtiendo todos los datos enviados por POST en un objeto FormData para que puedan ser recibidos por el servidor en el formato correcto.

Finalmente en la linea 22 se configura la cabecera “Content-Type” como “undefined” para que ésta sea determinada de manera automática, ya que si colocamos manualmente “multipart/form-data”, el valor límite no será establecido y el servidor no será capaz de procesar correctamente la petición.

 

Asignar el archivo a un modelo

Una vez que hemos hecho los ajustes para que los archivos sean enviados correctamente, lo siguiente que tenemos que hacer, es definir una directiva para que una vez seleccionado un archivo desde el input file, éste se asigne a nuestro modelo para posteriormente ser enviado por el $resource.

angular.module('myApp')
    .directive('fileModel', [function() {
        return {
            controller: ['$parse', '$element', '$attrs', '$scope', function($parse, $element, $attrs, $scope){
                var exp = $parse($attrs.filesModel);

                $element.on('change', function(){
                    exp.assign($scope, this.files);
                    $scope.$apply();
                });
            }]
        }
    }]);

Ahora en nuestro código html podemos usar la directiva file-model de la siguiente forma:

<input type="file" class="hidden" file-model="user.avatar"/>

 

Limitantes

Esta solución no permite enviar objetos que contengan mas de un nivel, por ejemplo: {user: {name: 'john', lastname: 'doe'}}, ya que solo procesa los valores del primer nivel, aunque honestamente, he podido realizar proyectos relativamente complejos con esta solución sin ningún problema.

Conclusión

Como ya comenté esta solución me ha funcionado muy bien, sin embargo, entiendo que pueda haber otras formas de resolverlo que yo desconozco, así que si tu lo has resuelto de otra forma, no dudes en compartirla en los comentarios.



Publicaciones que pueden interesarte

    Deja un comentario

      tope
    Derechos Reservados, Merida Design 2017
    %d bloggers like this: