AngularJS:使用$资源上载文件(解决方案) [英] AngularJS: Upload files using $resource (solution)
问题描述
我使用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 的文件的内容code>(这将是角的标准行为)。
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.
警告这有一定的局限性:
- 它不工作,在旧的浏览器。对不起:(
-
如果你的模式已经嵌入的文件,如
- It doesn't work on older browsers. Sorry :(
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 $resource
s 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屋!