AngularJS: $resource를 사용하여 파일 업로드(솔루션)
노출된 다양한 엔터티를 추상화하는 데 RESTful
사용하여 웹 서비스 와 상호 작용하기 위해 AngularJS를 사용 $resource
하고 있습니다. 이 엔터티 중 일부는 이미지이므로 동일한 요청 내에서 이진 데이터와 텍스트 필드를 모두 보내려면 "객체" save
작업 을 사용할 수 있어야 합니다 $resource
.
AngularJS의 $resource
서비스를 사용 하여 단일 POST
요청으로 데이터를 보내고 이미지를 편안한 웹 서비스에 업로드하려면 어떻게 해야 합니까?
나는 광범위하게 검색했고 놓쳤을 수도 있지만 $resource 작업을 사용하여 파일을 업로드하는 이 문제에 대한 해결책을 찾지 못했습니다.
이 예를 만들어 보겠습니다. RESTful 서비스를 사용하면 /images/
끝점에 요청하여 이미지에 액세스할 수 있습니다 . 각각 Image
에는 제목, 설명 및 이미지 파일을 가리키는 경로가 있습니다. RESTful 서비스를 사용하여 모두( GET /images/
), 하나( GET /images/1
) 또는 하나 추가( POST /images
)할 수 있습니다. Angular를 사용하면 $resource 서비스를 사용하여 이 작업을 쉽게 수행할 수 있지만 세 번째 작업에 필요한 파일 업로드를 즉시 허용 하지 않습니다. 곧 ). 그렇다면 파일 업로드를 처리할 수 없는 매우 편리한 $resource 서비스를 사용하는 방법은 무엇입니까? 그것은 아주 쉬운 것으로 밝혀졌습니다!
AngularJS의 멋진 기능 중 하나이기 때문에 데이터 바인딩을 사용할 것입니다. 다음 HTML 양식이 있습니다.
<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>
보시다시피 input
단일 개체의 속성에 각각 바인딩된 두 개의 텍스트 필드가 있습니다 newImage
. 파일 input
은 newImage
객체 의 속성에도 바인딩 되지만 이번에는 here 에서 직접 가져온 사용자 지정 지시문을 사용했습니다 . 이 지시문은 파일의 input
내용이 변경 될 때마다 FileList 객체가 fakepath
(Angular의 표준 동작이 되는) 대신 bind된 속성 내부에 놓 이도록 합니다 .
컨트롤러 코드는 다음과 같습니다.
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 서버를 실행하고 있으며 응답은 status
필드와 선택적 data
필드를 포함하는 json 객체 입니다).
파일 업로드가 작동하려면 $http 서비스를 적절하게 구성하면 됩니다(예: 앱 개체의 .config 호출 내). 특히 각 게시 요청의 데이터를 FormData 개체로 변환하여 올바른 형식으로 서버에 보내야 합니다.
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;
});
Content-Type
헤더로 설정 undefined
을 수동으로 설정하기 때문에 multipart/form-data
경계 값을 설정할 것, 그리고 서버가 제대로 요청을 구문 분석 할 수 없습니다.
그게 다야 이제 표준 데이터 필드와 파일을 모두 포함 $resource
하는 save()
개체에 사용할 수 있습니다 .
경고 다음과 같은 몇 가지 제한 사항이 있습니다.
- 이전 브라우저에서는 작동하지 않습니다. 죄송합니다 :(
모델에 다음과 같은 문서가 "포함된" 경우
{ title: "A title", attributes: { fancy: true, colored: false, nsfw: true }, image: null }
그런 다음 그에 따라 transformRequest 함수를 리팩토링해야 합니다. 예를 들어
JSON.stringify
다른 쪽 끝에서 구문 분석할 수 있는 경우 중첩된 개체를 사용할 수 있습니다.영어는 제 모국어가 아니므로 설명이 불명확한 경우 다시 말씀해 주세요. :)
- 이것은 단지 예일 뿐입니다. 애플리케이션이 수행해야 하는 작업에 따라 이를 확장할 수 있습니다.
도움이 되었기를 바랍니다.
편집하다:
@david가 지적한 것처럼 덜 침습적인 솔루션은 $resource
실제로 필요한 경우 에만 이 동작을 정의 하고 AngularJS의 모든 요청을 변환하지 않는 것입니다. 다음 $resource
과 같이 생성 하면 됩니다.
$resource('http://localhost:3000/images/:id', {id: "@_id"}, {
save: {
method: 'POST',
transformRequest: '<THE TRANSFORMATION METHOD DEFINED ABOVE>',
headers: '<SEE BELOW>'
}
});
헤더는 요구 사항을 충족하는 헤더를 만들어야 합니다. 지정해야 하는 것은 'Content-Type'
속성을 로 설정하는 것뿐입니다 undefined
.
$resource
요청 을 보내는 가장 최소한의 최소 침습 솔루션은 다음 과 FormData
같습니다.
angular.module('app', [
'ngResource'
])
.factory('Post', function ($resource) {
return $resource('api/post/:id', { id: "@id" }, {
create: {
method: "POST",
transformRequest: angular.identity,
headers: { 'Content-Type': undefined }
}
});
})
.controller('PostCtrl', function (Post) {
var self = this;
this.createPost = function (data) {
var fd = new FormData();
for (var key in data) {
fd.append(key, data[key]);
}
Post.create({}, fd).$promise.then(function (res) {
self.newPost = res;
}).catch(function (err) {
self.newPostError = true;
throw err;
});
};
});
이 방법은 1.4.0 이상에서 작동하지 않습니다. 자세한 내용은 AngularJS 변경 로그 (검색
$http: due to 5da1256
) 및 이 문제 를 확인하세요 . 이것은 실제로 AngularJS에서 의도하지 않은(따라서 제거된) 동작이었습니다.
양식 데이터를 FormData 개체로 변환(또는 추가)하기 위해 이 기능을 생각해 냈습니다. 서비스로 활용될 수 있을 것 같습니다.
아래 논리는 transformRequest
, 또는 $httpProvider
구성 내부 에 있거나 서비스로 사용될 수 있습니다. 어떤 방식으로든 Content-Type
헤더는 NULL로 설정되어야 하며 그렇게 하는 것은 이 논리를 배치하는 컨텍스트에 따라 다릅니다. 예를 들어 transformRequest
리소스를 구성할 때 옵션 내부 에서 다음을 수행합니다.
var headers = headersGetter();
headers['Content-Type'] = undefined;
또는 구성하는 경우 $httpProvider
위의 답변에 언급된 방법을 사용할 수 있습니다.
아래 예에서 논리는 transformRequest
리소스에 대한 메서드 내부에 배치 됩니다.
appServices.factory('SomeResource', ['$resource', function($resource) {
return $resource('some_resource/:id', null, {
'save': {
method: 'POST',
transformRequest: function(data, headersGetter) {
// Here we set the Content-Type header to null.
var headers = headersGetter();
headers['Content-Type'] = undefined;
// And here begins the logic which could be used somewhere else
// as noted above.
if (data == undefined) {
return data;
}
var fd = new FormData();
var createKey = function(_keys_, currentKey) {
var keys = angular.copy(_keys_);
keys.push(currentKey);
formKey = keys.shift()
if (keys.length) {
formKey += "[" + keys.join("][") + "]"
}
return formKey;
}
var addToFd = function(object, keys) {
angular.forEach(object, function(value, key) {
var formKey = createKey(keys, key);
if (value instanceof File) {
fd.append(formKey, value);
} else if (value instanceof FileList) {
if (value.length == 1) {
fd.append(formKey, value[0]);
} else {
angular.forEach(value, function(file, index) {
fd.append(formKey + '[' + index + ']', file);
});
}
} else if (value && (typeof value == 'object' || typeof value == 'array')) {
var _keys = angular.copy(keys);
_keys.push(key)
addToFd(value, _keys);
} else {
fd.append(formKey, value);
}
});
}
addToFd(data, []);
return fd;
}
}
})
}]);
따라서 이를 통해 문제 없이 다음을 수행할 수 있습니다.
var data = {
foo: "Bar",
foobar: {
baz: true
},
fooFile: someFile // instance of File or FileList
}
SomeResource.save(data);
ReferenceURL : https://stackoverflow.com/questions/21115771/angularjs-upload-files-using-resource-solution
'IT이야기' 카테고리의 다른 글
MapKey 대 HasForeignKey 차이점 - Fluent Api (0) | 2021.09.30 |
---|---|
셸 스크립트의 명령줄을 통해 예상에 인수를 전달하는 방법 (0) | 2021.09.30 |
EPPlus를 사용한 Excel 날짜 형식 (0) | 2021.09.30 |
dataTable() 대 DataTable() - 차이점이 있는 이유와 함께 작동 (0) | 2021.09.30 |
Haskell에서 새 모나드를 정의하면 Applicative에 대한 인스턴스가 발생하지 않습니다. (0) | 2021.09.29 |