Home - About me - Browse by categories

Création d’un multipart file stream provider personnalisé avec ASPNET Web API

La gestion de l’upload de fichier dans un service ASP.NET Web API se fait à l’aide d’un MultipartFileStreamProvider. Il est possible de créer ses propres providers pour enregistrer un fichier d’une manière particulière, par exemple dans un blob azure ou une base de données SQL Server. C’est le deuxième exemple que j’ai choisi pour illustrer ce post.

Utilisation basique du MultipartFileStreamProvider

Pour cet exemple, on se place dans un ApiController tout ce qu’il y a de plus classique, dans une méthode appelée en POST :

[HttpPost]
public async Task<HttpResponseMessage> Post()
{
if (!Request.Content.IsMimeMultipartContent("form-data"))
return Request.CreateErrorResponse(HttpStatusCode.UnsupportedMediaType, "contenu non supporté");

//le multipart provider va télécharger les fichiers dans le répertoire temporaire
var multipartProvider = new MultipartFileStreamProvider(Path.GetTempPath());
var files = await Request.Content.ReadAsMultipartAsync(multipartProvider)
.ContinueWith(t =>
{
if (t.IsFaulted)
throw t.Exception;

//retourne la liste des fichiers qui ont été téléchargés sur le systeme de fichier local
//dans le répertoire temporaire
return t.Result.FileData.ToList();
});

foreach (var file in files)
{
//chemin du fichier en local :
string path = file.LocalFileName;

//nom du fichier envoyé :
string fileName = file.Headers.ContentDisposition.FileName;

//contentType du fichier
string contentType = file.Headers.ContentType.MediaType;
}

return Request.CreateResponse(HttpStatusCode.OK);
}

Il est d’abord nécessaire de vérifier que le POST a bien été fait avec un mime content en form-data. Si ce n’est pas le cas, on retourne en indiquant que le format du média n’est pas supporté.

Dans un second temps, il suffit d’instancier un MultipartFileStreamProvider en lui passant un répertoire de base pour télécharger les fichiers (ici un dossier temporaire) et d’appeler la méthode ReadAsMultipartAsync sur l’objet Request. Il est alors possible de récupérer les différents fichiers, leur chemin d’accès local, leur content type, leur nom… Il est alors ensuite possible d’appliquer n’importe quel traitement sur le fichier pour l’exploiter (envoie dans un blob azure, stockage dans une base de données…). Ce qui va devenir vraiment intéressant est de factoriser ces différents traitements dans des multipart providers dédiés à ces tâches spécifiques !

Personnalisation d’un MultipartFileStreamProvider

Il est possible de dériver la classe MultipartFileStreamProvider afin de surcharger la méthode ExecutePostProcessingAsync, en charge de traiter les différents fichier qui sont envoyés sur le serveur. Dans le cas présent, cette méthode va être surchargée pour envoyer automatiquement les fichiers dans une base de données SQL Serveur.

Pour commencer, on créé un constructeur qui permet de passer la chaîne de connexion vers la base de données ainsi que le fournisseur à utiliser :

public class MultipartSqlFileStreamProvider : MultipartFileStreamProvider
{
private const string SQL = "INSERT INTO Files (FileId, ContentType, FileName, FileContent) VALUES (@FileId, @ContentType, @FileName, @FileContent)";

private readonly string _connectionString;
private readonly DbProviderFactory _dbProviderFactory;

public List<Guid> FileIDs{ get; set; }

public MultipartSqlFileStreamProvider(string connectionString, string providerName)
: base(Path.GetTempPath())
{
_connectionString = connectionString;
FileIDs = new List<Guid>();
_dbProviderFactory = DbProviderFactories.GetFactory(providerName);
}
}

Ensuite, on surcharge la méthode et on parcourt la liste de fichier afin de les enregistrer dans la base SQL :

public async override Task ExecutePostProcessingAsync()
{
using (var sqlConnection = _dbProviderFactory.CreateConnection())
{
sqlConnection.ConnectionString = _connectionString;
sqlConnection.Open();

foreach (var file in FileData)
{
using (var dbCommand = sqlConnection.CreateCommand())
{
dbCommand.CommandText = SQL;

Guid fileId = Guid.NewGuid();

var fileIdParameter = dbCommand.CreateParameter();
fileIdParameter.ParameterName = "FileId";
fileIdParameter.Value = fileId;
fileIdParameter.DbType = System.Data.DbType.Guid;
dbCommand.Parameters.Add(fileIdParameter);

var contentTypeParameter = dbCommand.CreateParameter();
contentTypeParameter.ParameterName = "ContentType";
contentTypeParameter.Value = file.Headers.ContentType.MediaType;
contentTypeParameter.DbType = System.Data.DbType.StringFixedLength;
dbCommand.Parameters.Add(contentTypeParameter);

var fileNameParameter = dbCommand.CreateParameter();
fileNameParameter.ParameterName = "FileName";
fileNameParameter.Value = file.Headers.ContentDisposition.FileName.Replace("\"","");
fileNameParameter.DbType = System.Data.DbType.StringFixedLength;
dbCommand.Parameters.Add(fileNameParameter);

var fileContentParameter = dbCommand.CreateParameter();
fileContentParameter.ParameterName = "FileContent";
fileContentParameter.Value = File.ReadAllBytes(file.LocalFileName);
fileContentParameter.DbType = System.Data.DbType.Binary;
dbCommand.Parameters.Add(fileContentParameter);

await dbCommand.ExecuteNonQueryAsync();

FileIDs.Add(fileId);
}
}

sqlConnection.Close();
}
}

Pour utiliser le multipart provider personnalisé, il suffit de l’instancier et de le passer à la méthode ReadAsMultipartAsync, comme vu en introduction de cet article.

var connectionString = WebConfigurationManager.ConnectionStrings["DataConnectionString"];

var multipartSqlProvider = new MultipartSqlFileStreamProvider(connectionString.ConnectionString, connectionString.ProviderName);
var fileIDs = await Request.Content.ReadAsMultipartAsync(multipartSqlProvider)
.ContinueWith(t =>
{
if (t.IsFaulted)
{
throw t.Exception;
}

var provider = t.Result;
return provider.OutputFileIDs.First().ToString();
}
);

Dès lors, les fichiers sont automatiquement envoyés dans la base de données et vous récupérez en output la liste des IDs des fichiers !

Enjoy Clignement d'œil

Julien

read more

Déployer automatiquement vos applications ASPNET MVC dans Azure avec Team Foundation Service

Team Foundation Service (anciennement TFS Preview) est la version hébergée de Team Foundation Server. Pour en savoir plus sur ce service proposé par Microsoft, je vous invite à lire cet article et cet article sur le blog d’cet article !

Une des fonctionnalité très sympa lorsque l’on développe des applications destinées à être hébergées dans Windows Azure, est la possibilité d’associer automatiquement un projet d’équipe Team Foundation Service à un Cloud Service Azure. Cela permet de faire en sorte qu’à chaque check-in depuis Visual Studio, le code soit compilé et automatiquement déployé dans Azure.

Pour associer un projet d’équipe à un service de compute Azure, il faut se connecter sur l’interface de management Windows Azure puis se rendre sur le tableau de bord du service en question :

image

Cliquez sur Configurer la publication TFS. La popup qui s’ouvre vous permet soit d’utiliser un compte TFService déjà existant, soit de créer un compte sur visualstudio.com :

image

Saisissez le nom de votre compte de service si vous en avez déjà un. Si ce n’est pas le cas, cliquez sur Créez un compte TFS maintenant. La popup qui s’ouvre vous permet de créer un compte TFS Service :

image

Créez votre compte. Pour les utilisateur existant, cliquez sur le lien Autorisez maintenant.

Une fenêtre s’ouvre alors pour vous demander d’autoriser votre service Windows Azure à accéder à TFS, un peu comme cela pourrait être pour n’importe quel membre de l’équipe :

image

Si vous êtes d’accord, acceptez Sourire Vous pouvez à présent choisir le projet d’équipe à associer :

image

Patientez pendant la liaison du projet d’équipe à votre service Windows Azure :

image

Une fois ceci fait, ouvrez Visual Studio et connectez- vous à votre projet d’équipe. Dans le Team Explorer, affichez les définitions Builds configurées. Comme vous pouvez le constater, une nouvelle Build est présente. Elle porte le nom de votre service Cloud Azure, suffixé par _CD pour Continuous Deployment :

image

Si vous ouvrez la définition de la build, vous pourrez voir que celle ci est configurée pour être exécuté à chaque check-in (Continuous Integration), dans l’onglet Trigger. Si vous allez dans l’onglet Process, vous constaterez que le process template utilisé est un spécifique à Windows Azure : AzureContinuousDeployment.11.xaml. En ouvrant le workflow de build, et en fouillant un peu dans les différentes activités, vous finirez par trouver la partie qui concerne le déploiement dans Azure :

image

Concrètement, vous n’avez rien à modifier pour que ça marche Clignement d'œil

Afin d’avoir la main sur le profil de publication qui doit être utilisé par la build lors du déploiement, vous devez en créer un (ou utiliser un existant, si vous en avez déjà un). Pour se faire, faites un clic droit sur votre projet Cloud dans Visual Studio et cliquez sur Publish…

Dans la fenêtre qui s’ouvre, choisissez votre souscription Azure :

image

Configurez ensuite les paramètres en choisissez votre Cloud Service, l’environnement sur lequel vous souhaitez déployer (Production / Staging), la build configuration et la service configuration à utiliser :

image

Sur la page de résumé, enregistrez le profile, mais ne cliquez pas sur Publish, cliquez sur Cancel pour quitter :

image

Visual Studio a créé un fichier XML qui représente ce profil, dans le dossier Profiles du projet Windows Azure. Retournez dans la définition de la build, dans l’onglet Process et déroulez la section** 5. Publishing – Azure Cloud Service. **Modifiez le paramètre Alternate Publish Profile pour le faire pointer sur le fichier de profil que vous venez de créer :

image

Enregistrez les paramètres, et lancez la build. Le tour est joué.

Note : pour le moment le SDK 1.8 de Windows Azure n’est pas pris en charge par l’agent de build hosté sur Team Foundation Service, mais cela devrait arriver. En attendant, il est possible d’indiquer que la build doit s’exécuter sur un agent de build configuré on-premise, par vos soins.

Enjoy Clignement d'œil

Julien

read more

Azure Service Bus, BrokeredMessage et opérations longues

Le service de messaging d’Azure Service Bus n’est à l’origine pas prévu pour exécuter des long-running process. Cependant le SDK 1.8 sorti en Octobre dernier apporte une petite nouveauté sur ce point. En effet, la classe BrokeredMessage offre désormais une méthode RenewLock qui permet de demander le renouvèlement d’un verrou sur le brokered message afin qu’il ne soit pas remis à disposition dans la file d’attente.

Du coup, en se basant sur la propriété LockedUntilUtc, on peut réussir à exécuter une opération longue sans avoir de problème de perte de verrou sur le message :

//création d'un CTS pour lancer un task en charge de renouveller le verrou du message
var brokeredMessageRenewCancellationTokenSource = new CancellationTokenSource();

try
{
//reception du message
var brokeredMessage = _client.Receive();

var brokeredMessageRenew = Task.Factory.StartNew(() =>
{
while (!brokeredMessageRenewCancellationTokenSource.Token.IsCancellationRequested)
{
//on se base sur la propriété LockedUntilUtc pour savoir si le verrou expire bientôt
if (DateTime.UtcNow > brokeredMessage.LockedUntilUtc.AddSeconds(-10))
{
//si oui, on renouvelle le message
brokeredMessage.RenewLock();
}

Thread.Sleep(500);
}
}, brokeredMessageRenewCancellationTokenSource.Token);

//exécution d'une opération longue
RunLongRunningOperation();

//on marque le message comme terminé
brokeredMessage.Complete();
}
catch(MessageLockLostException)
{
//le verrou a expiré
}
catch(Exception)//autre exception
{
//autre erreur : on abandonne le message
brokeredMessage.Abandon();
}
finally
{
//on arrête la task de renouvellement du verrou
brokeredMessageRenewCancellationTokenSource.Cancel();
}

Et voilà ! Clignement d'œil

Julien

read more

Introduction à Windows Azure Media Services

Windows Azure Media Services

Il s’agit d’une nouvelle brique de Windows Azure qui permet de gérer l’hébergement, l’encodage, la mise à disposition de médias, sur Internet. Il est notamment possible de gérer des workflows de publication de vidéo. Par exemple, vous allez pouvoir choisir d’envoyer un fichier vidéo au format HD dans un espace de stockage Windows Azure (blob), et faire en sorte que ce fichier soit automatiquement encodé dans différents formats (basse résolution etc…) et publié sur Internet. Pour se faire, Microsoft propose un ensemble d’API dans le SDK Windows Azure Media Services. ### Création d’un service de média

Connectez-vous sur le site de management Windows Azure et cliquez sur la section « Services de média » imageCliquez ensuite sur le lien « Créer un service de média ». Vous devrez alors fournir trois informations : - Le nom du service que vous souhaitez créer (chiffres et lettre, tout en minuscules) - La localisation géographique - Le stockage Windows Azure qui doit être utilisé pour stocker les fichiers

image

Patientez ensuite jusqu’à ce que le service soit créé. Une fois ceci fait, il est possible de récupérer la clé de management qui permettra d’utiliser le SDK pour envoyer des fichiers vidéos sur Azure et les traiter, au sein d’une application .NET. Pour cela, rendez-vous sur le dashboard du service de média que vous venez de créer et cliquez sur le lien « Gérer les clés » : imageCopiez alors la clé d’accès primaire que vous allez réutiliser par la suite. ### Utilisation de l’API .NET

Afin de pouvoir utiliser les APIs Windows Azure Media Service, il est nécessaire d’inclure ce package Nuget dans votre projet. Le premier objet à instancier est un CloudMediaContext. C’est lui qui représente le point d’entrée de l’API Windows Azure Media Services. Il est nécessaire de passer le nom du service ainsi que la clé d’accès primaire au constructeur :```csharp var context = new CloudMediaContext(“nom de votre service Azure Media Services”, “votre clé”);

Via ce context, vous allez pouvoir créer un nouvel asset. Un asset représente une vidéo, dans les différents formats sous lesquels elle sera publié. Pour créer un asset, il faut exécuter le code suivant :

```csharp
string videoFilePath = @"E:\VIDEOS\Test.mp4";
string fileName = Path.GetFileName(videoFilePath);
string assetName = string.Format("{0} - {1}", Path.GetFileNameWithoutExtension(videoFilePath), DateTime.Now);

//création de l'asset sur le CloudMediaContext
IAsset asset = context.Assets.Create(assetName, AssetCreationOptions.StorageEncrypted);

L’asset est a présent crée, il est alors possible d’envoyer le fichier vidéo qui est associé :

IAssetFile file = asset.AssetFiles.Create(fileName);
file.Upload(videoFilePath);

Vous pouvez vous arrêter à ce code si vous ne souhaitez pas ré encoder le fichier derrière pour le publier dans différents formats. D’ailleurs, si vous vous connectez à l’interface de gestion de Windows Azure Media Services, vous devriez voir apparaitre le contenu vidéo qui a été envoyé.

Dans le cas présent, nous allons pousser un peu plus loin puisque nous allons lancer un job d’encodage. Pour se faire, il est nécessaire de récupérer un media processor, c’est à dire un outils qui va permettre d’effectuer l’encodage vidéo. Windows Azure Media Services en propose plusieurs :

  • Windows Azure Media Encoder
  • Windows Azure Media Packager
  • Windows Azure Media Encryptor
  • Storage Decryption

Pour plus d’info sur les media processors, consultez cette page de la MSDN.

Ici, c’est le premier de la liste qui va être utilisé. Pour le récupérer, il suffit de faire une simple requête LINQ sur la collection MediaProcessors du contexte :

var mediaProcessor = context.MediaProcessors.Where(f => f.Name == "Windows Azure Media Encoder").AsEnumerable().FirstOrDefault();

Afin de lancer une tâche d’encodage, il est également nécessaire de préciser une configuration à utiliser. Il est possible de récupérer les différentes configuration sur cette page, en fonction du media processor utilisé ! Dans le cas présent, c’est le preset H.264 256k DSL CBR qui va être utilisé :

string encodingConfiguration = "H.264 256k DSL CBR";

Il est désormais possible de créer la tâche d’encodage :

//création du job
IJob job = context.Jobs.Create(string.Format("job d'encodage de {0}", asset.Name));

//ajout de la tâche d'encodage
ITask task = job.Tasks.AddNew(string.Format("tâche d'encodage de {0}", asset.Name), mediaProcessor, encodingConfiguration, TaskOptions.None);

//ajout de l'asset en input
task.InputAssets.Add(asset);

//création de l'asset d'output
var outputAsset = task.OutputAssets.AddNew(string.Format("{0} (output)", asset.Name), true, AssetCreationOptions.None);

//soumission du job
job.Submit();

Pour attendre la fin du job, il faut récupérer une task sur l’objet job à l’aide de la méthode GetExecutionProgressionTask. Il est alors possible de tester son état pour savoir s’il a réussi ou échoué :

//attente de la fin du job
Task jobProgressionTask = job.GetExecutionProgressTask(CancellationToken.None);
jobProgressionTask.Wait();

//test de l'état du job
if (job.State == JobState.Finished)
{
Console.WriteLine("Le job est terminé");
}
else if (job.State == JobState.Error)
{
Console.WriteLine("Une erreur est survenue pendant l'exécution du job.");
}

Il est également possible de s’abonner à l’événement StateChanged sur le job afin de récupérer les différentes transitions au fur à mesur de son exécution :

static void job_StateChanged(object sender, JobStateChangedEventArgs e)
{
Console.WriteLine("L'état du job vient de passer de {0} à {1}", e.PreviousState, e.CurrentState);
}

Et voilà, le tour est joué, vous avez pu envoyer un fichier vidéo directement dans le stockage Azure et lancer différents workflow pour réencoder la vidéo à la volée avant de la publier :

image

Et si vous allez sur l’interface Windows Azure vous pouvez constater que l’output est bien présent et publiable :

image

Un fois publié, il est possible de lire lire et de récupérer son adresse dans le blob storage Azure :

image

Enjoy Clignement d'œil

Julien

read more

[Windows Phone 8] Ecrire un tag NFC et lancement d’une application via NFC

Dans mon article précédent, je vous montrais comment utiliser l’API de proximity device pour échanger une information entre deux téléphones lors d’un tap NFC. Dans ce nouvel article, je vais vous montrer comment écrire du contenu dans un tag NFC afin de le réexploiter pour qu’il puisse fournir une URL ou encore qu’il permette de lancer une application !

Ecriture d’un tag

L’écriture d’un tag se fait très simplement. Tout d’abord, Il faut récupérer une instance de ProximityDevice grace à la méthode statique GetDefault de cette même classe :

public partial class MainPage : PhoneApplicationPage
{
private readonly ProximityDevice _proximityDevice;
// Constructor
public MainPage()
{
InitializeComponent();
_proximityDevice = ProximityDevice.GetDefault();
}
}

Une fois récupéré, le ProximityDevice permet d’écouter les messages NFC apportés par un tag sur lequel il est possible d’écrire. Pour celà, il suffit de souscrire aux messages de type <em>Writeable</em> :

private long subId = 0;
protected override void OnNavigatedTo(NavigationEventArgs e)
{
if (_proximityDevice == null)
{
Dispatcher.BeginInvoke(() =>
{
MessageBox.Show("Le NFC n'est pas supporté sur ce téléphone", "Pas de NFC", MessageBoxButton.OK);
});
}
else
{
subId = _proximityDevice.SubscribeForMessage("WriteableTag", OnWriteableTagArrived);
}

base.OnNavigatedTo(e);
}

Dans le handler OnWriteableTagArrived, on récupère le ProximityDevice qui lève l’événement, et on peut alors écrire une information à l’aide de la méthode PublishBinaryMessage :

private long pubId = 0;
private void OnWriteableTagArrived(ProximityDevice sender, ProximityMessage message)
{
var dataWriter = new DataWriter();
dataWriter.UnicodeEncoding = Windows.Storage.Streams.UnicodeEncoding.Utf16LE;
dataWriter.WriteString("http://www.juliencorioland.net");
pubId = sender.PublishBinaryMessage("WindowsUri:WriteTag", dataWriter.DetachBuffer());
}

Comme vous pouvez le constater, j’utilise ici le protocole WindowsUri:WriteTag, qui me permet d’écrire une information de type URL dans le tag NFC (ici il s’agit d’une carte de dev offerte par Nokia à la //build/), en l’occurrence ici l’adresse de mon blog. Du coup, lorsque je passe mon téléphone devant le tag NFC, celui ci affiche me propose d’afficher mon blog :

image

Lorsque j’accepte, le navigateur s’ouvre sur mon blog !

Lancer son application à partir d’un tag NFC

Un des cas d’utilisation de NFC prévu par Microsoft sous Windows Phone 8 est la possibilité d’associer une application à un type de tag, permettant ainsi de lancer une application par contact NFC. Par exemple, vous pouvez imaginer une borne intéractive à l’entrée d’un magasin, sur laquelle vous poser votre téléphone qui lance alors l’application pour ce magasin en particulier, avec du contenu mis en avant !

Association de protocole

Dans un premier temps, il faut créer une association de protocole pour votre application. La première étape consiste à ajouter une extension dans la manifest de l’application :

<Extensions>
<Protocol Name="samplenfc" TaskID="_default" NavUriFragment="encodedLaunchUri=%s" />
</Extensions>

Ici, on associe le protocole samplenfc: à l’application (les deux autres paramètres sont les valeurs à renseigner obligatoirement, cf. http://msdn.microsoft.com/en-us/library/windowsphone/develop/jj206987(v=vs.105).aspx#BKMK_Protocolassociations).

Ensuite, il faut créer un UriMapper capable de traiter l’association de protocole. Pour cela on créé une classe qui dérive de UriMapperBase :

public class AssociationUriMapper : UriMapperBase
{
public override Uri MapUri(Uri uri)
{
string url = HttpUtility.UrlDecode(uri.ToString());

if (url.Contains("samplenfc:MainPage"))
{
int paramIndex = url.IndexOf("source=") + 7;
string paramValue = url.Substring(paramIndex);

return new Uri("/MainPage.xaml?source=" + paramValue, UriKind.Relative);
}

return uri;
}
}

Le code est assez simple ici : je récupère l’URL samplenfc:qqchose et je la converti en URL XAML.

Ecriture du tag NFC

Pour l’écriture du tag NFC, cela se passe à peu près comme dans la première partie de l’article, sauf que cette fois, le protocole n’est plus http, mais samplenfc :

private long pubId = 0;
private void OnWriteableTagArrived(ProximityDevice sender, ProximityMessage message)
{
var dataWriter = new DataWriter();
dataWriter.UnicodeEncoding = Windows.Storage.Streams.UnicodeEncoding.Utf16LE;
//dataWriter.WriteString("http://www.juliencorioland.net");
//pubId = sender.PublishBinaryMessage("WindowsUri:WriteTag", dataWriter.DetachBuffer());

string appLauncher = string.Format(@"samplenfc:MainPage?source=NFCWakeUp");
dataWriter.WriteString(appLauncher);
pubId = sender.PublishBinaryMessage("WindowsUri:WriteTag", dataWriter.DetachBuffer());
}

Du coup, après avoir écrit mon tag sur la carte de développement, lorsque j’approche mon téléphone de celle-ci, j’obtiens bien le résultat attendu : le téléphone me propose de lancer mon application :

</param></embed> NFC Rocks ! Julien
read more