AngularJS:使用$资源上载文件(解决方案) [英] AngularJS: Upload files using $resource (solution)

查看:138
本文介绍了AngularJS:使用$资源上载文件(解决方案)的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我使用AngularJS与一个 REST风格的 Web服务交互,使用 $资源抽象暴露的各种实体。一些这种实体是图像,所以我需要能够使用 $资源对象的保存行动对同一个请求中同时发送二进制数据和文本字段。

I'm using AngularJS to interact with a RESTful webservice, using $resource to abstract the various entities exposed. Some of this entities are images, so I need to be able to use the save action of $resource "object" to send both binary data and text fields within the same request.

如何使用AngularJS的 $资源服务来发送数据,并上传图片到一个RESTful Web服务的一个 POST 请求?

How can I use AngularJS's $resource service to send data and upload images to a restful webservice in a single POST request?

推荐答案

我已经搜查甚广,虽然我可能已经错过了它,我无法找到这个问题的解决方案:使用$资源上传文件行动。

I've searched far and wide and, while I might have missed it, I couldn't find a solution for this problem: uploading files using a $resource action.

让我们把这个例子:我们的RESTful服务可以让我们通过请求到 /图像/ 端点访问图像。每个图片有一个标题,描述和指向图像文件的路径。使用RESTful服务,我们可以得到所有的人( GET /图像/ ),一个一( GET /影像/ 1 )或添加一个( POST /图像)。角允许我们使用$资源服务来完成这个任务容易,但不允许文件上载 - 这是所必需的第三个动作 - 开箱(和they似乎并没有被打算近期内支持它)。那么,如何将我们去使用非常方便$资源服务,如果它不能处理文件上传?事实证明,这是很容易的!

Let's make this example: our RESTful service allows us to access images by making requests to the /images/ endpoint. Each Image has a title, a description and the path pointing to the image file. Using the RESTful service, we can get all of them (GET /images/), a single one (GET /images/1) or add one (POST /images). Angular allows us to use the $resource service to accomplish this task easily, but doesn't allow for file uploading - which is required for the third action - out of the box (and they don't seem to be planning on supporting it anytime soon). How, then, would we go about using the very handy $resource service if it can't handle file uploads? It turns out it's quite easy!

我们将使用数据绑定,因为它是AngularJS的真棒特点之一。我们有以下的HTML表单:

We are going to use data binding, because it's one of the awesome features of AngularJS. We have the following HTML form:

<form class="form" name="form" novalidate ng-submit="submit()">
    <div class="form-group">
        <input class="form-control" ng-model="newImage.title" placeholder="Title" required>
    </div>
    <div class="form-group">
        <input class="form-control" ng-model="newImage.description" placeholder="Description">
    </div>
    <div class="form-group">
        <input type="file" files-model="newImage.image" required >
    </div>

    <div class="form-group clearfix">
        <button class="btn btn-success pull-right" type="submit" ng-disabled="form.$invalid">Save</button>
    </div>
</form>

正如你所看到的,但是也有一些绑定每到一个单一的对象,这是我所谓的属性两个文本输入字段 newImage 。文件输入被绑定以及在 newImage 对象的属性,但是这一次我已经使用了自定义从指令直取这里。该指令使得这样每次输入的变化,一个文件列表对象放在绑定物业内,而不是一个 fakepath (这将是角的标准行为)。

As you can see, there are two text input fields that are binded each to a property of a single object, which I have called newImage. The file input is binded as well to a property of the newImage object, but this time I've used a custom directive taken straight from here. This directive makes it so that every time the content of the file input changes, a FileList object is put inside the binded property instead of a fakepath (which would be Angular's standard behavior).

我们的控制器code是以下内容:

Our controller code is the following:

angular.module('clientApp')
.controller('MainCtrl', function ($scope, $resource) {
    var Image = $resource('http://localhost:3000/images/:id', {id: "@_id"});

    Image.get(function(result) {
        if (result.status != 'OK')
            throw result.status;

        $scope.images = result.data;
    })

    $scope.newImage = {};

    $scope.submit = function() {
        Image.save($scope.newImage, function(result) {
            if (result.status != 'OK')
                throw result.status;

            $scope.images.push(result.data);
        });
    }
}); 

(在这种情况下,我在我的本地机器上3000端口运行的NodeJS服务器,响应是包含状态字段和一个可选的<$ JSON对象C $ C>数据字段)。

(In this case I am running a NodeJS server on my local machine on port 3000, and the response is a json object containing a status field and an optional data field).

为了使文件上传的工作,我们只需要在应用程序对象上的.config调用中正确配置$ http服务,例如。具体而言,我们需要将每个POST请求的数据转换到FORMDATA对象,因此,它的向服务器发送的正确格式:

In order for the file upload to work, we just need to properly configure the $http service, for example within the .config call on the app object. Specifically, we need to transform the data of each post request to a FormData object, so that it's sent to the server in the correct format:

angular.module('clientApp', [
'ngCookies',
'ngResource',
'ngSanitize',
'ngRoute'
])
.config(function ($httpProvider) {
  $httpProvider.defaults.transformRequest = function(data) {
    if (data === undefined)
      return data;

    var fd = new FormData();
    angular.forEach(data, function(value, key) {
      if (value instanceof FileList) {
        if (value.length == 1) {
          fd.append(key, value[0]);
        } else {
          angular.forEach(value, function(file, index) {
            fd.append(key + '_' + index, file);
          });
        }
      } else {
        fd.append(key, value);
      }
    });

    return fd;
  }

  $httpProvider.defaults.headers.post['Content-Type'] = undefined;
});

内容类型头设置为未定义,因为手动将它设置为多部分/ form-data的将不设置边界值,服务器将无法正确解析请求。

The Content-Type header is set to undefined because setting it manually to multipart/form-data would not set the boundary value, and the server would not be able to parse the request correctly.

就是这样。现在你可以使用 $资源保存()含有标准的数据字段和文件的对象。

That's it. Now you can use $resource to save() objects containing both standard data fields and files.

警告这有一定的局限性:


  1. 它不工作,在旧的浏览器。对不起:(

  2. 如果你的模式已经嵌入的文件,如

  1. It doesn't work on older browsers. Sorry :(
  2. If your model has "embedded" documents, like

{
   标题:一个称号
   属性:{
     花哨的:真实,
     有色:假的,
     NSFW:真
   },
   图片:空
}

那么你就需要相应的重构transformRequest功能。 您可以,例如, JSON.stringify 嵌套的对象,只要你可以分析他们在另一端

then you need to refactor the transformRequest function accordingly. You could, for example, JSON.stringify the nested objects, provided you can parse them on the other end

英语不是我的主要语言,所以,如果我的解释是模糊的告诉我,我会尝试重组吧:)

English is not my main language, so if my explanation is obscure tell me and I'll try to rephrase it :)

我希望这有助于,干杯!

I hope this helps, cheers!

正如 @大卫指出的,微创的办法是只定义为那些<$ C此行为$ C> $资源 S中的实际需要,而不是改造每一个请求由AngularJS的。你可以通过创建你的 $资源是这样的:

As pointed out by @david, a less invasive solution would be to define this behavior only for those $resources that actually need it, and not to transform each and every request made by AngularJS. You can do that by creating your $resource like this:

$resource('http://localhost:3000/images/:id', {id: "@_id"}, { 
    save: { 
        method: 'POST', 
        transformRequest: '<THE TRANSFORMATION METHOD DEFINED ABOVE>', 
        headers: '<SEE BELOW>' 
    } 
});

至于标题,你应该创建一个满足您的要求。你需要指定的唯一的事情是'的Content-Type属性将其设置为未定义

这篇关于AngularJS:使用$资源上载文件(解决方案)的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆