ファイルを$httpでアップロードする

2016-03-01

AngularJS1.5でファイルをアップロードする機能を追加したかったのだが、ファイルをアップロードしてもページ遷移はしたくなかったので、$httpを使ってAJAX的にプログラム内から送って、結果を受け取るようにした。

例として定義するモジュール

定義するモジュール名を仮にappとする:

// app.js
angular.module('app', [])

選択したファイルをAngularJSに渡せるようにするためのディレクティブを定義する

HTMLのinput type="file"で選択したファイルをAngularJSに受け渡すためのディレクティブを定義する:

// app.js
angular.module('app')
.directive('fileModel', ['$parse', function($parse) {
return {
restrict: 'A',
link: function(scope, element, attrs) {
const model = $parse(attrs.fileModel)
element.bind('change', function() {
scope.$apply(() => {
model.assign(scope, element[0].files[0])
})
})
},
}
}])
  • elementchangeを監視して、変更されたらモデルを更新する

ファイルを選択してアップロードするためのHTML

上で定義したfile-modelディレクティブを使って、ファイルを選択するinputと、アップロードするためのボタンを用意する:

// index.html
<!DOCTYPE html>
<html ng-app="app">
<head>
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.5.0/angular.min.js"></script>
<script src="app.js" type="text/javascript"></script>
</head>
<body ng-controller="AppController as appCtrl">
<input type="file" file-model="myFile">
<button ng-click="myFile&&appCtrl.upload(myFile)" ng-disabled="!myFile">Upload</button>
</body>
</html>
  • コントローラAppControllerは後述
  • 選択したファイルをmyFileというモデル(変数名)に格納することにする
  • ボタンが押されたらappCtrluploadメソッドに、選択されたファイルmyFileを渡す

アップロードするサービス

ファイルを$httpで送る機能をAngularJSのサービスとして作成する:

// app.js
angular.module('app')
.service('fileUpload', ['$http', function($http) {
this.uploadFileToUrl = function(uploadUrl, name, file) {
const formData = new FormData()
formData.append(name, file)
const config = {
headers: {
'Content-Type': undefined,
},
}
return $http.post(uploadUrl, formData, config)
}
}])
  • $http.postを使う
  • Content-Typeundefinedを指定すると良きに計らってくれるらしい

ファイルをアップロードする

上で作成したファイルをアップロードするサービスを使って、コントローラ内からファイルをアップロードする:

// app.js
angular.module('app')
.controller('AppController', ['fileUpload', function(fileUpload) {
this.upload = (file) => {
fileUpload.uploadFileToUrl('/upload', 'upName', file)
.then(
(response) => {
console.log('upload success')
console.log(response.data)
},
(response) => {
console.error('upload failed')
console.log(response)
})
}
}])
  • 依存関係にfileUploadを含めて、それを使う