Esiste una procedura precisa per l'upload dei file con AngularJS e Node.js.
Installiamo multer per la negoziazione e gestione dei file in upload:
npm install multer
Quindi lo usiamo in questo modo:
var express = require('express');
var app = express();
var multer = require('multer');
var port = process.env.PORT || 8000;
var storage = multer.diskStorage({
destination: function (req, file, cb) {
cb(null, 'uploads/'); // Le immagini verranno uploadate qui
},
filename: function (req, file, cb) {
cb(null, file.originalname); // Vogliamo che l'immagine salvata mantenga il nome originale
}
});
var upload = multer({ storage: storage });
// 'image' è il valore dell'attributo name dell'input
app.post('/api/upload', upload.single('image'), function(req,res, next) {
var output = {};
output.file = req.file;
res.header('Content-Type', 'application/json');
res.end(JSON.stringify(output));
});
app.listen(port);
In Angular creiamo un servizio che usi le API HTML5 per la gestione dei dati dei form. In pratica la variabile fd
conterrà il nome del file e i contenuti dello stesso:
angular.module('MyApp').factory('FileUploader', ['$http', function ($http) {
return {
upload: function(file, url, callback) {
var fd = new FormData();
fd.append('image', file);
$http.post(url, fd, {
transformRequest: angular.identity,
headers: {'Content-Type': undefined}
}).then(function(response){
callback(response);
}, function(errorResponse){
// Gestione errore
});
}
};
}]);
A questo punto dobbiamo leggere il file scelto sull'input creando una direttiva personalizzata che acceda alle API HTML5 quando il valore dell'input cambia:
angular.module('MyApp').directive('fileModel', ['$parse', function ($parse) {
return {
restrict: 'A', // Come attributo
link: function(scope, element, attrs) {
var model = $parse(attrs.fileModel);
var modelSetter = model.assign;
element.bind('change', function(){ // Evento change sull'input file
scope.$apply(function(){
modelSetter(scope, element[0].files[0]); // Usiamo le API HTML5 per ottenere il file selezionato
});
});
}
};
}]);
Il nostro form di upload mostrerà un messaggio quando l'upload è terminato. Notate come la nostra direttiva vengaa usata come attributo sull'input del file:
<div id="image-upload" ng-controller="UploadCtrl">
<form id="upload" enctype="multipart/form-data" ng-submit="upload(image)">
<p><input class="form-control" type="file" file-model="image" name="image" /></p>
<p><input type="submit" class="btn btn-primary" value="Upload" /></p>
<div ng-show="uploaded" class="bg-success">Uploaded {{uploadedImage}}</div>
</form>
</div>
L'ultimo step è creare il controller associato:
angular.module('MyApp').controller('UploadCtrl', ['$scope', 'FileUploader', function($scope, FileUploader) {
$scope.uploaded = false;
$scope.uploadedImage = '';
$scope.upload = function(image) {
FileUploader.upload(image, '/api/upload', function(resp) {
if(resp && resp.file) {
$scope.uploadedImage = resp.file.originalname;
$scope.uploaded = true;
} else {
// Gestione errore
}
});
};
}]);