Reputation: 314
Hi All,
My environment : Angular 1.2.10 - Breeze - .Net - Entity-Framework ADO .Net 6.0
I've seen a lot of posts for file upload (alone) such as https://github.com/danialfarid/angular-file-upload I'm trying to upload a file within all of a form, but without success.
Would you have any idea ?
I'm doing mvvm on angular-side, it gives :
Angular - html :
<section id="register-view" class="mainbar" data-ng-controller="register as vm">
<form name="RegisterForm" ng-submit="submit()" data-n ng-controller="vm.Ctrl">
<section class="matter">
<div class="container">
<div class="row">
<div class="col-md-12">
<ul class="today-datas">
<li class="blightblue">
<div class="pull-left"><i class="fa fa-plane"></i></div>
<div class="datas-text pull-right">
<span class="bold">11, 12, 13 Février 2014<br />Microsoft Techdays, France</span>
</div>
<div class="clearfix"></div>
</li>
</ul>
</div>
</div>
<div class="row">
<div class="col-md-6">
<div class="widget wviolet">
<div data-cc-widget-header title="{{vm.title}}"
allow-collapse="true"></div>
<div class="widget-content text-center text-info">
<table class="table table-condensed table-striped">
<tbody>
<tr>
<td>Nom utilisateur</td>
<td><input ng-model="vm.user.UserName" required /></td>
<td></td>
</tr>
<tr>
<td>Mot de passe</td>
<td><input ng-model="vm.user.Membership.Password" required type="password" /></td>
<td></td>
</tr>
<tr>
<td>Confirmation mot de passe</td>
<td><input ng-model="vm.user.Membership.ConfirmPassword" required type="password" /></td>
<td></td>
</tr>
<tr>
<td>Photo <strong>(facultatif)</strong></td>
<td>
<!--<input ng-model="vm.user.Photo" type="file" />-->
<div ng-controller="vm.CtrlPhoto">
<input type="file" id="fileToUpload" ng-file-select="onFileSelect($files)" />
</div>
<!--<div id="dropbox" class="dropbox" ng-class="dropClass"><span>Drop files here...</span></div>-->
</td>
<td></td>
</tr>
<tr>
<td>Prénom</td>
<td><input ng-model="vm.user.FirstName" required /></td>
<td></td>
</tr>
<tr>
<td>Nom</td>
<td><input ng-model="vm.user.LastName" required /></td>
<td></td>
</tr>
<tr>
<td>Email</td>
<td><input ng-model="vm.user.Membership.Email" required type="email" /></td>
<td></td>
</tr>
<tr>
<td>Téléphone <strong>(facultatif)</strong></td>
<td><input ng-model="vm.user.Phone" type="tel" /></td>
<td></td>
</tr>
<tr>
<td>Date de naissance <strong>(facultatif)</strong></td>
<td>
<!--<input ng-model="vm.user.Phone" type="tel" />-->
<select>
<option value="0"></option>
<option value="1">1</option>
<option value="2">2</option>
<option value="3">3</option>
<option value="4">4</option>
<option value="5">5</option>
<option value="6">6</option>
<option value="7">7</option>
<option value="8">8</option>
<option value="9">9</option>
<option value="10">10</option>
<option value="11">11</option>
<option value="12">12</option>
<option value="13">13</option>
<option value="14">14</option>
<option value="15">15</option>
<option value="16">16</option>
<option value="17">17</option>
<option value="18">18</option>
<option value="19">19</option>
<option value="20">20</option>
<option value="21">21</option>
<option value="22">22</option>
<option value="23">23</option>
<option value="24">24</option>
<option value="25">25</option>
<option value="26">26</option>
<option value="27">27</option>
<option value="28">28</option>
<option value="29">29</option>
</select>
<select>
<option value="1">Janvier</option>
<option value="2">Février</option>
<option value="3">Mars</option>
<option value="4">Avril</option>
<option value="5">Mai</option>
<option value="6">Juin</option>
<option value="7">Juillet</option>
<option value="8">Août</option>
<option value="9">Septembre</option>
<option value="10">Octobre</option>
<option value="11">Novembre</option>
<option value="12">Décembre</option>
</select>
</td>
<td></td>
</tr>
<tr>
<td>Position</td>
<td><textarea ng-model="vm.user.Position" wrap="hard" rows="5" cols="60" /></td>
<td></td>
</tr>
<tr>
<td>Intérêts</td>
<td><textarea ng-model="vm.user.Interests" rows="5" cols="60" /></td>
<td><input type="submit" id="submit" value="Submit" /></td>
</tr>
</tbody>
</table>
</div>
<div class="widget-foot">
<div class="clearfix"></div>
</div>
</div>
</div>
<div class="col-md-6" />
</div>
</div>
</section>
</form>
</section>
The js viewModel :
(function () {
'use strict';
var controllerId = 'register';
angular.module('app').controller(controllerId, ['common', 'datacontext', 'entityManagerFactory', register]);
function register(common, datacontext, entityManagerFactory) {
var getLogFn = common.logger.getLogFn;
var log = getLogFn(controllerId);
var vm = this;
vm.title = 'Register';
vm.Ctrl = Ctrl;
vm.CtrlPhoto = CtrlPhoto;
var user = {
ApplicationId: "1",
UserId: "",
UserName: "",
LoweredUserName: "",
MobileAlias: "",
IsAnonymous: false,
LastActivityDate: "",
FirstName: "",
LastName: "",
BirthDate: "",
CountryCode: "",
Phone: "",
Photo: "",
PathPhoto: "",
Position: "",
Interests: "",
Application: "",
Membership: {
ApplicationId: "1",
UserId: "",
Password: "",
PasswordFormat: "",
PasswordSalt: "",
MobilePIN: "",
Email: "",
LoweredEmail: "",
PasswordQuestion: "",
PasswordAnswer: "",
IsApproved: "",
IsLockedOut: "",
CreateDate: "",
LastLoginDate: "",
LastPasswordChangedDate: "",
LastLockoutDate: "",
FailedPasswordAttemptCount: "",
FailedPasswordAttemptWindowStart: "",
FailedPasswordAnswerAttemptCount: "",
FailedPasswordAnswerAttemptWindowStart: "",
Comment: "",
Application: "",
User: ""
},
Missions: "",
PersonalizationPerUsers: "",
Profile: "",
WebpagesOAuthMemberships: "",
Roles: "",
PostedFile: ""
};
vm.user = user;
activate();
function activate() {
common.activateController(null, controllerId)
.then(function () {
log('Activated Register View');
});
}
function CtrlPhoto($scope) {
$scope.onFileSelect = function ($files) {
//$files: an array of files selected, each file has name, size, and type.
for (var i = 0; i < $files.length; i++) {
var file = $files[i];
vm.user.PostedFile = file;
//$scope.upload = $upload.upload({
// url: '/IdPhoto', //upload.php script, node.js route, or servlet url
// // method: POST or PUT,
// // headers: {'headerKey': 'headerValue'},
// // withCredential: true,
// data: vm.myModelObj,
// file: file,
// // file: $files, //upload multiple files, this feature only works in HTML5 FromData browsers
// /* set file formData name for 'Content-Desposition' header. Default: 'file' */
// //fileFormDataName: myFile, //OR for HTML5 multiple upload only a list: ['name1', 'name2', ...]
// /* customize how data is added to formData. See #40#issuecomment-28612000 for example */
// //formDataAppender: function(formData, key, val){} //#40#issuecomment-28612000
//}).progress(function (evt) {
// console.log('percent: ' + parseInt(100.0 * evt.loaded / evt.total));
//}).success(function (data, status, headers, config) {
// // file is uploaded successfully
// console.log(data);
//});
//.error(...)
//.then(success, error, progress);
}
}
}
function Ctrl($scope) {
$scope.submit = function () {
//vm.user.Photo = document.getElementById("fileToUpload");
datacontext.Register(vm.user)
//.then(loadFile($upload))
.then(common.logger.log("File posted"))
.catch(failCallback)
//$upload.upload({
// url: '~/IdPhoto',
// file: vm.user.PathPhoto // for single file
//})
//.then(function (data) {
// vm.user.PathPhoto.fileId = data;
//})
};
function loadFile($upload) {
//var fd = new FormData();
var fileToUpload = document.getElementById("fileToUpload");
//fd.append("uploadedFile", fileToUpload);
//var xhr = new XMLHttpRequest();
//xhr.open("POST", '/IdPhoto');
//xhr.send(fd)
$upload.upload({
url: '/IdPhoto',
file: fileToUpload,
}).then(function (data, status, headers, config) {
// file is uploaded successfully
//console.log(data);
}).catch(failCallback(error));
}
}
function failCallback(error) {
var msg = 'Error Posting File ' + error.message;
common.logger.logError(msg, error);
throw error;
}
}
})();
By the ApiController - BreezeController side the post method gives:
[System.Web.Http.AcceptVerbs("POST")]
//[HttpPost]
public string Register(User user)
{
System.Web.Security.MembershipCreateStatus status = MembershipCreateStatus.UserRejected;
//pour les nouvelles inscriptions il n'y a pas de nwpassword et de confirmnewpassword
//ModelState.Remove("user.Membership.NewPassword");
//ModelState.Remove("user.Membership.ConfirmNewPassword");
//if (ModelState.IsValid)
//{
// Tentative d'inscription de l'utilisateur
try
{
CVAppMembershipProvider provider = (CVAppMembershipProvider)System.Web.Security.Membership.Providers["CVAppMembershipProvider"];
var httpContext = (HttpContextWrapper)Request.Properties["MS_HttpContext"];
var foo = httpContext.Request.Form["Foo"];
MembershipUser usr = provider.CreateUser(user, out status);
if (status == MembershipCreateStatus.Success)
{
FormsAuthenticationTicket ticket = new FormsAuthenticationTicket(1,
usr.UserName,
DateTime.Now,
DateTime.Now.AddMonths(1),
false,
"", //userData
FormsAuthentication.FormsCookiePath);
////Encrypt the ticket.
string encTicket = FormsAuthentication.Encrypt(ticket);
var resp = new HttpResponseMessage();
//create and set cookie in response
var cookie = new CookieHeaderValue(FormsAuthentication.FormsCookieName, encTicket);
cookie.Expires = DateTimeOffset.Now.AddMonths(1);
cookie.Domain = Request.RequestUri.Host;
cookie.Path = "/";
resp.Headers.AddCookies(new CookieHeaderValue[] { cookie });
}
else
{
ModelState.AddModelError(status.ToString(), GetErrorMessage(status));
}
}
catch (Exception e)
{
ModelState.AddModelError("", e.Message);
}
//}
return status.ToString();
}
In this post method I would like to do some treatments on the user (JSon Format) spent by the viewModel, this is OK. In the same time I would like to upload the file from the form (element 'fileToUpload' from the angular form). Is it possible ?
All the examples found over internet don't give an answer.
I suppose the Json format for serialization is not able to spend the file.
XML serialization would help ?
Any simpler solution ?
Thanx in advance for your help
Upvotes: 0
Views: 933
Reputation: 314
I found a solution to my problem. It consists to do only the UploadFile, to spend the user to the UploadFile (change the name ==> Register), it gives in the datacontext (javascript) :
function Register($upload, user, postedFile) {
return $q.when(
$upload.upload({
url: 'CVApp/User/Register', //upload.php script, node.js route, or servlet url
method: 'POST',
// headers: {'headerKey': 'headerValue'},
// withCredential: true,
data: user,
file: postedFile,
// file: $files, //upload multiple files, this feature only works in HTML5 FromData browsers
/* set file formData name for 'Content-Desposition' header. Default: 'file' */
//fileFormDataName: myFile, //OR for HTML5 multiple upload only a list: ['name1', 'name2', ...]
/* customize how data is added to formData. See #40#issuecomment-28612000 for example */
//formDataAppender: function(formData, key, val){} //#40#issuecomment-28612000
}
)
.then(successCallback)
.catch(failCallback) // same as 'then(null, failCallback)'
.finally(finalCallback) // sort of like 'then(finalCallback, finalCallback)'
);
}
on the server-side, it gives in the ApiController :
[System.Web.Http.AcceptVerbs("POST")]
public async Task<HttpResponseMessage> Register()
{
// Check if the request contains multipart/form-data.
if (!Request.Content.IsMimeMultipartContent())
{
throw new HttpResponseException(HttpStatusCode.UnsupportedMediaType);
}
string root = HttpContext.Current.Server.MapPath("~/IdPhoto");
var provider = new MultipartFormDataStreamProvider(root);
try
{
// Read the form data.
await Request.Content.ReadAsMultipartAsync(provider);
User user = new User(provider.FormData);
System.Web.Security.MembershipCreateStatus status = MembershipCreateStatus.UserRejected;
CVAppMembershipProvider mProvider = (CVAppMembershipProvider)System.Web.Security.Membership.Providers["CVAppMembershipProvider"];
MembershipUser usr = mProvider.CreateUser(user, out status);
if (status == MembershipCreateStatus.Success)
{
FormsAuthenticationTicket ticket = new FormsAuthenticationTicket(1,
usr.UserName,
DateTime.Now,
DateTime.Now.AddMonths(1),
false,
"", //userData
FormsAuthentication.FormsCookiePath);
////Encrypt the ticket.
string encTicket = FormsAuthentication.Encrypt(ticket);
var resp = new HttpResponseMessage();
//create and set cookie in response
var cookie = new CookieHeaderValue(FormsAuthentication.FormsCookieName, encTicket);
cookie.Expires = DateTimeOffset.Now.AddMonths(1);
cookie.Domain = Request.RequestUri.Host;
cookie.Path = "/";
resp.Headers.AddCookies(new CookieHeaderValue[] { cookie });
}
else
{
throw new Exception(status.ToString() + " - " + GetErrorMessage(status));
}
// This illustrates how to get the file names.
foreach (MultipartFileData file in provider.FileData)
{
string fileName = file.Headers.ContentDisposition.FileName;
if (fileName.StartsWith("\"") && fileName.EndsWith("\""))
{
fileName = fileName.Trim('"');
}
if (fileName.Contains(@"/") || fileName.Contains(@"\"))
{
fileName = Path.GetFileName(fileName);
}
fileName = user.UserName.Replace(" ", "_") + "." + fileName.Substring(fileName.IndexOf(".") + 1);
File.Copy(file.LocalFileName, Path.Combine(root, fileName));
}
return Request.CreateResponse(HttpStatusCode.OK);
}
catch (Exception e)
{
//return Request.CreateErrorResponse(HttpStatusCode.InternalServerError, e);
throw e;
}
}
Et voilà ! It works.
Now I just need to have the "BusyIndicator" while processing, to have the error / right message at the end, and also to be able to execute my async methods in the order I want (if it's possible, I thought the "then" could do this), I'm still searching, I suppose I'll find this fastly on Internet.
Thanx
Upvotes: 0