Partager via


Inclusion d’une option de chargement de fichier lors de l’ajout d’un nouvel enregistrement (C#)

par Scott Mitchell

Télécharger le PDF

Ce tutoriel montre comment créer une interface web qui permet à l’utilisateur d’entrer des données texte et de charger des fichiers binaires. Pour illustrer les options disponibles pour stocker des données binaires, un fichier est enregistré dans la base de données, tandis que l’autre est stocké dans le système de fichiers.

Introduction

Dans les deux tutoriels précédents, nous avons exploré les techniques de stockage des données binaires associées au modèle de données de l’application, nous avons examiné comment utiliser le contrôle FileUpload pour envoyer des fichiers du client au serveur web et nous avons vu comment présenter ces données binaires dans un contrôle Web de données. Nous n’avons pas encore parlé de la façon d’associer des données chargées au modèle de données.

Dans ce tutoriel, nous allons créer une page web pour ajouter une nouvelle catégorie. Outre les zones de texte pour le nom et la description de la catégorie, cette page doit inclure deux contrôles FileUpload, l’un pour la nouvelle image de catégorie et l’autre pour la brochure. L’image chargée est stockée directement dans la nouvelle colonne d’enregistrement Picture , tandis que la brochure est enregistrée dans le ~/Brochures dossier avec le chemin d’accès au fichier enregistré dans la nouvelle colonne d’enregistrement BrochurePath .

Avant de créer cette page web, nous devons mettre à jour l’architecture. La CategoriesTableAdapter requête s main ne récupère pas la Picture colonne. Par conséquent, la méthode générée automatiquement Insert a uniquement des entrées pour les CategoryNamechamps , Descriptionet BrochurePath . Par conséquent, nous devons créer une méthode supplémentaire dans TableAdapter qui vous invite à entrer les quatre Categories champs. La CategoriesBLL classe de la couche logique métier doit également être mise à jour.

Étape 1 : Ajout d’uneInsertWithPictureméthode auCategoriesTableAdapter

Lorsque nous avons créé le CategoriesTableAdapter dans le didacticiel Création d’une couche d’accès aux données, nous l’avons configuré pour générer INSERTautomatiquement des instructions , UPDATEet DELETE en fonction de la requête main. En outre, nous avons demandé à TableAdapter d’utiliser l’approche DB Direct, qui a créé les méthodes Insert, Updateet Delete. Ces méthodes exécutent les instructions , UPDATEet DELETE générées automatiquement et, par conséquent, acceptent les paramètres d’entrée en fonction des colonnes retournées INSERTpar la requête main. Dans le didacticiel Chargement de fichiers, nous avons augmenté la CategoriesTableAdapter requête s main pour utiliser la BrochurePath colonne.

Étant donné que la CategoriesTableAdapter requête s main ne fait pas référence à la Picture colonne, nous ne pouvons ni ajouter un nouvel enregistrement ni mettre à jour un enregistrement existant avec une valeur pour la Picture colonne. Pour capturer ces informations, nous pouvons soit créer une nouvelle méthode dans le TableAdapter qui est utilisée spécifiquement pour insérer un enregistrement avec des données binaires, soit personnaliser l’instruction générée automatiquement INSERT . Le problème avec la personnalisation de l’instruction générée automatiquement INSERT est que nous risquons d’avoir nos personnalisations remplacées par l’Assistant. Par exemple, imaginez que nous avons personnalisé l’instruction pour inclure l’utilisation INSERT de la Picture colonne. Cela met à jour la méthode TableAdapter pour Insert inclure un paramètre d’entrée supplémentaire pour les données binaires de l’image de catégorie. Nous pourrions ensuite créer une méthode dans la couche de logique métier pour utiliser cette méthode DAL et appeler cette méthode BLL via la couche de présentation, et tout fonctionnerait à merveille. Autrement dit, jusqu’à la prochaine fois que nous avons configuré TableAdapter via l’Assistant Configuration de TableAdapter. Dès que l’Assistant est terminé, nos personnalisations de l’instruction INSERT sont remplacées, la Insert méthode revient à son ancienne forme et notre code ne se compile plus !

Notes

Cette gêne n’est pas un problème lors de l’utilisation de procédures stockées au lieu d’instructions SQL ad hoc. Un prochain tutoriel explorera l’utilisation de procédures stockées au lieu d’instructions SQL ad hoc dans la couche d’accès aux données.

Pour éviter ce problème potentiel, plutôt que de personnaliser les instructions SQL générées automatiquement, nous allons plutôt créer une méthode pour TableAdapter. Cette méthode, nommée InsertWithPicture, accepte les valeurs pour les CategoryNamecolonnes , Description, BrochurePathet Picture et et exécute une INSERT instruction qui stocke les quatre valeurs dans un nouvel enregistrement.

Ouvrez le DataSet typé et, à partir du Designer, cliquez avec le bouton droit sur l’en-tête CategoriesTableAdapter et choisissez Ajouter une requête dans le menu contextuel. Cela lance l’Assistant Configuration de requête TableAdapter, qui commence par nous demander comment la requête TableAdapter doit accéder à la base de données. Choisissez Utiliser des instructions SQL, puis cliquez sur Suivant. L’étape suivante demande le type de requête à générer. Étant donné que nous créons une requête pour ajouter un nouvel enregistrement à la Categories table, choisissez INSERT, puis cliquez sur Suivant.

Sélectionnez l’option INSERT

Figure 1 : Sélectionnez l’option INSERT (Cliquer pour afficher l’image en taille réelle)

Nous devons maintenant spécifier l’instruction INSERT SQL. L’Assistant suggère automatiquement une INSERT instruction correspondant à la requête main TableAdapter. Dans ce cas, il s’agit d’une INSERT instruction qui insère les CategoryNamevaleurs , Descriptionet BrochurePath . Mettez à jour l’instruction afin que la Picture colonne soit incluse avec un @Picture paramètre, comme suit :

INSERT INTO [Categories] 
    ([CategoryName], [Description], [BrochurePath], [Picture]) 
VALUES 
    (@CategoryName, @Description, @BrochurePath, @Picture)

Le dernier écran de l’Assistant nous demande de nommer la nouvelle méthode TableAdapter. Entrez InsertWithPicture et cliquez sur Terminer.

Nommez la nouvelle méthode TableAdapter InsertWithPicture

Figure 2 : Nommez la nouvelle méthode InsertWithPicture TableAdapter (Cliquez pour afficher l’image en taille réelle)

Étape 2 : Mise à jour de la couche de logique métier

Étant donné que la couche de présentation ne doit s’interfacer qu’avec la couche logique métier au lieu de la contourner pour accéder directement à la couche d’accès aux données, nous devons créer une méthode BLL qui appelle la méthode DAL que nous venons de créer (InsertWithPicture). Pour ce tutoriel, créez une méthode dans la CategoriesBLL classe nommée InsertWithPicture qui accepte comme entrée trois string s et un byte tableau. Les string paramètres d’entrée sont pour le nom de catégorie, la description et le chemin du fichier de brochure, tandis que le byte tableau concerne le contenu binaire de l’image de catégorie. Comme le montre le code suivant, cette méthode BLL appelle la méthode DAL correspondante :

[System.ComponentModel.DataObjectMethodAttribute
    (System.ComponentModel.DataObjectMethodType.Insert, false)] 
public void InsertWithPicture(string categoryName, string description, 
    string brochurePath, byte[] picture)
{
    Adapter.InsertWithPicture(categoryName, description, brochurePath, picture);
}

Notes

Vérifiez que vous avez enregistré le DataSet typé avant d’ajouter la InsertWithPicture méthode à la BLL. Étant donné que le CategoriesTableAdapter code de classe est généré automatiquement en fonction du DataSet typé, si vous n’enregistrez pas d’abord vos modifications dans le DataSet typé, la Adapter propriété ne connaîtra pas la InsertWithPicture méthode .

Étape 3 : Répertorier les catégories existantes et leurs données binaires

Dans ce tutoriel, nous allons créer une page qui permet à un utilisateur final d’ajouter une nouvelle catégorie au système, en fournissant une image et une brochure pour la nouvelle catégorie. Dans le tutoriel précédent , nous avons utilisé un GridView avec un Champ de modèle et un Champ d’image pour afficher le nom, la description, l’image et un lien de chaque catégorie pour télécharger sa brochure. Nous allons répliquer cette fonctionnalité pour ce tutoriel, en créant une page qui répertorie toutes les catégories existantes et permet de créer de nouvelles catégories.

Commencez par ouvrir la DisplayOrDownload.aspx page à partir du BinaryData dossier. Accédez à la vue Source et copiez la syntaxe déclarative gridView et ObjectDataSource, en la collant dans l’élément <asp:Content> dans UploadInDetailsView.aspx. En outre, n’oubliez pas de copier la GenerateBrochureLink méthode de la classe code-behind de DisplayOrDownload.aspx vers UploadInDetailsView.aspx.

Copiez et collez la syntaxe déclarative de DisplayOrDownload.aspx vers UploadInDetailsView.aspx

Figure 3 : Copier et coller la syntaxe déclarative de DisplayOrDownload.aspx à UploadInDetailsView.aspx (Cliquer pour afficher l’image en taille réelle)

Après avoir copié la syntaxe déclarative et GenerateBrochureLink la méthode sur la UploadInDetailsView.aspx page, affichez la page via un navigateur pour vous assurer que tout a été copié correctement. Vous devriez voir un GridView répertoriant les huit catégories qui incluent un lien pour télécharger la brochure ainsi que l’image de catégorie.

Vous devriez maintenant voir chaque catégorie avec ses données binaires

Figure 4 : Vous devez maintenant voir chaque catégorie avec ses données binaires (cliquez pour afficher l’image en taille réelle)

Étape 4 : Configuration de pour prendre en charge l’insertionCategoriesDataSource

L’ObjetDataSource CategoriesDataSource utilisé par GridView Categories n’offre actuellement pas la possibilité d’insérer des données. Pour prendre en charge l’insertion via ce contrôle de source de données, nous devons mapper sa Insert méthode à une méthode dans son objet sous-jacent, CategoriesBLL. En particulier, nous voulons le mapper à la CategoriesBLL méthode que nous avons ajoutée à l’étape 2, InsertWithPicture.

Commencez par cliquer sur le lien Configurer la source de données à partir de la balise active ObjectDataSource. Le premier écran montre l’objet avec lequel la source de données est configurée pour fonctionner, CategoriesBLL. Laissez ce paramètre tel qu’il est et cliquez sur Suivant pour passer à l’écran Définir des méthodes de données. Accédez à l’onglet INSERT et choisissez la InsertWithPicture méthode dans la liste déroulante. Cliquez sur Terminer pour terminer l'Assistant.

Configurer ObjectDataSource pour utiliser la méthode InsertWithPicture

Figure 5 : Configurer ObjectDataSource pour utiliser la InsertWithPicture méthode (Cliquer pour afficher l’image en taille réelle)

Notes

À la fin de l’Assistant, Visual Studio peut vous demander si vous souhaitez actualiser les champs et les clés, ce qui régénérera les champs des contrôles web de données. Choisissez Non, car le choix de Oui remplacera toutes les personnalisations de champ que vous avez effectuées.

Une fois l’Assistant terminé, ObjectDataSource inclut désormais une valeur pour sa InsertMethod propriété ainsi que InsertParameters pour les quatre colonnes de catégorie, comme l’illustre le balisage déclaratif suivant :

<asp:ObjectDataSource ID="CategoriesDataSource" runat="server" 
    OldValuesParameterFormatString="original_{0}" SelectMethod="GetCategories" 
    TypeName="CategoriesBLL" InsertMethod="InsertWithPicture">
    <InsertParameters>
        <asp:Parameter Name="categoryName" Type="String" />
        <asp:Parameter Name="description" Type="String" />
        <asp:Parameter Name="brochurePath" Type="String" />
        <asp:Parameter Name="picture" Type="Object" />
    </InsertParameters>
</asp:ObjectDataSource>

Étape 5 : Création de l’interface d’insertion

Comme indiqué dans La vue d’ensemble de l’insertion, de la mise à jour et de la suppression de données, le contrôle DetailsView fournit une interface d’insertion intégrée qui peut être utilisée lors de l’utilisation d’un contrôle de source de données qui prend en charge l’insertion. Nous allons ajouter un contrôle DetailsView à cette page au-dessus de GridView qui restitue définitivement son interface d’insertion, ce qui permet à un utilisateur d’ajouter rapidement une nouvelle catégorie. Lors de l’ajout d’une nouvelle catégorie dans DetailsView, le GridView en dessous s’actualise automatiquement et affiche la nouvelle catégorie.

Commencez par faire glisser un DetailsView de la boîte à outils vers le Designer au-dessus du GridView, en définissant sa ID propriété NewCategory sur et en effaçant les valeurs de propriété Height et Width . À partir de la balise active detailsView, liez-la à l’existantCategoriesDataSource, puis case activée la case à cocher Activer l’insertion.

Capture d’écran montrant DetailsView ouvert avec la propriété CategoryID définie sur NewCategory, les valeurs des propriétés Height et Width vides et la case à cocher Activer l’insertion sélectionnée.

Figure 6 : Lier l’objet DetailsView à et Activer l’insertion CategoriesDataSource (cliquer pour afficher l’image en taille réelle)

Pour afficher définitivement detailsView dans son interface d’insertion, définissez sa DefaultMode propriété sur Insert.

Notez que DetailsView a cinq BoundFields CategoryID, CategoryName, DescriptionNumberOfProducts, et BrochurePath bien que le CategoryID BoundField ne soit pas rendu dans l’interface d’insertion, car sa InsertVisible propriété est définie sur false. Ces BoundFields existent, car il s’agit des colonnes retournées par la GetCategories() méthode , qui est ce que l’ObjectDataSource appelle pour récupérer ses données. Toutefois, pour l’insertion, nous ne voulons pas laisser l’utilisateur spécifier une valeur pour NumberOfProducts. De plus, nous devons leur permettre de télécharger une image pour la nouvelle catégorie ainsi que de télécharger un FICHIER PDF pour la brochure.

Supprimez complètement l’objet NumberOfProducts BoundField du DetailsView, puis mettez à jour les HeaderText propriétés de CategoryName et BrochurePath de BoundFields en Catégorie et Brochure, respectivement. Ensuite, convertissez l’objet BrochurePath BoundField en templateField et ajoutez un nouveau TemplateField pour l’image, ce qui donne à ce nouveau TemplateField la HeaderText valeur Picture. Déplacez le Picture TemplateField de sorte qu’il se trouve entre templateField BrochurePath et CommandField.

Capture d’écran de la fenêtre champs avec TemplateField, Picture et HeaderText mis en surbrillance.

Figure 7 : Lier l’objet DetailsView à et Activer l’insertion CategoriesDataSource

Si vous avez converti l’objet BrochurePath BoundField en templateField via la boîte de dialogue Modifier les champs, le Champ de modèle inclut , ItemTemplateEditItemTemplateet InsertItemTemplate. Seul est InsertItemTemplate nécessaire, cependant, n’hésitez pas à supprimer les deux autres modèles. À ce stade, votre syntaxe déclarative de DetailsView doit ressembler à ce qui suit :

<asp:DetailsView ID="NewCategory" runat="server" AutoGenerateRows="False" 
    DataKeyNames="CategoryID" DataSourceID="CategoriesDataSource" 
    DefaultMode="Insert">
    <Fields>
        <asp:BoundField DataField="CategoryID" HeaderText="CategoryID" 
            InsertVisible="False" ReadOnly="True" 
            SortExpression="CategoryID" />
        <asp:BoundField DataField="CategoryName" HeaderText="Category" 
            SortExpression="CategoryName" />
        <asp:BoundField DataField="Description" HeaderText="Description" 
            SortExpression="Description" />
        <asp:TemplateField HeaderText="Brochure" SortExpression="BrochurePath">
            <InsertItemTemplate>
                <asp:TextBox ID="TextBox1" runat="server"
                    Text='<%# Bind("BrochurePath") %>'></asp:TextBox>
            </InsertItemTemplate>
        </asp:TemplateField>
        <asp:TemplateField HeaderText="Picture"></asp:TemplateField>
        <asp:CommandField ShowInsertButton="True" />
    </Fields>
</asp:DetailsView>

Ajout de contrôles FileUpload pour les champs de brochure et d’image

Actuellement, le BrochurePath TemplateField contient InsertItemTemplate un TextBox, tandis que le Picture TemplateField ne contient aucun modèle. Nous devons mettre à jour ces deux TemplateField s InsertItemTemplate pour utiliser les contrôles FileUpload.

Dans la balise active DetailsAfficher, choisissez l’option Modifier les modèles, puis sélectionnez templateField BrochurePath dans InsertItemTemplate la liste déroulante. Supprimez le contrôle TextBox, puis faites glisser un contrôle FileUpload de la boîte à outils vers le modèle. Définissez le contrôle FileUpload sur IDBrochureUpload. De même, ajoutez un contrôle FileUpload à TemplateField Picture de InsertItemTemplate. Définissez ce contrôle FileUpload sur IDPictureUpload.

Ajouter un contrôle FileUpload à l’objet InsertItemTemplate

Figure 8 : Ajouter un contrôle FileUpload à (InsertItemTemplateCliquer pour afficher l’image en taille réelle)

Après avoir effectué ces ajouts, les deux syntaxes déclaratives de TemplateField sont les suivantes :

<asp:TemplateField HeaderText="Brochure" SortExpression="BrochurePath">
    <InsertItemTemplate>
        <asp:FileUpload ID="BrochureUpload" runat="server" />
    </InsertItemTemplate>
</asp:TemplateField>
<asp:TemplateField HeaderText="Picture">
    <InsertItemTemplate>
        <asp:FileUpload ID="PictureUpload" runat="server" />
    </InsertItemTemplate>
</asp:TemplateField>

Lorsqu’un utilisateur ajoute une nouvelle catégorie, nous voulons nous assurer que la brochure et l’image sont du type de fichier correct. Pour la brochure, l’utilisateur doit fournir un fichier PDF. Pour l’image, nous avons besoin que l’utilisateur charge un fichier image, mais est-ce que nous autorisons n’importe quel fichier image ou uniquement les fichiers image d’un type particulier, tels que les fichiers GIF ou JPG ? Pour autoriser différents types de fichiers, nous devons étendre le Categories schéma afin d’inclure une colonne qui capture le type de fichier afin que ce type puisse être envoyé au client via Response.ContentType dans DisplayCategoryPicture.aspx. Étant donné que nous n’avons pas de colonne de ce type, il serait prudent de restreindre les utilisateurs à fournir uniquement un type de fichier image spécifique. Les Categories images existantes de la table sont des bitmaps, mais les fichiers JPG sont un format de fichier plus approprié pour les images servies sur le web.

Si un utilisateur charge un type de fichier incorrect, nous devons annuler l’insertion et afficher un message indiquant le problème. Ajoutez un contrôle Label Web sous detailsView. Définissez sa ID propriété sur UploadWarning, effacez sa Text propriété, définissez la CssClass propriété sur Avertissement et les propriétés et EnableViewState sur Visiblefalse. La Warning classe CSS est définie dans Styles.css et restitue le texte dans une grande police rouge, italique et en gras.

Notes

Dans l’idéal, les CategoryName boundFields et Description seraient convertis en TemplateFields et leurs interfaces d’insertion personnalisées. L’interface Description d’insertion, par exemple, serait probablement mieux adaptée via une zone de texte multiligne. Et étant donné que la CategoryName colonne n’accepte NULL pas les valeurs, un RequiredFieldValidator doit être ajouté pour s’assurer que l’utilisateur fournit une valeur pour le nom de la nouvelle catégorie. Ces étapes sont laissées comme un exercice au lecteur. Reportez-vous à Personnalisation de l’interface de modification des données pour une présentation détaillée de l’augmentation des interfaces de modification des données.

Étape 6 : Enregistrement de la brochure chargée dans le système de fichiers du serveur web

Lorsque l’utilisateur entre les valeurs d’une nouvelle catégorie et clique sur le bouton Insérer, une publication se produit et le flux de travail d’insertion se déroule. Tout d’abord, l’événement DetailsView se ItemInserting déclenche. Ensuite, la méthode ObjectDataSource est Insert() appelée, ce qui entraîne l’ajout d’un nouvel enregistrement à la Categories table. Après cela, l’événement DetailsView se ItemInserted déclenche.

Avant d’appeler la méthode ObjectDataSource Insert() , nous devons d’abord nous assurer que les types de fichiers appropriés ont été chargés par l’utilisateur, puis enregistrer le FICHIER PDF de la brochure dans le système de fichiers du serveur web. Créez un gestionnaire d’événements pour l’événement ItemInserting DetailsView et ajoutez le code suivant :

// Reference the FileUpload control
FileUpload BrochureUpload = 
    (FileUpload)NewCategory.FindControl("BrochureUpload");
if (BrochureUpload.HasFile)
{
    // Make sure that a PDF has been uploaded
    if (string.Compare(System.IO.Path.GetExtension
        (BrochureUpload.FileName), ".pdf", true) != 0)
    {
        UploadWarning.Text = 
            "Only PDF documents may be used for a category's brochure.";
        UploadWarning.Visible = true;
        e.Cancel = true;
        return;
    }
}

Le gestionnaire d’événements commence par référencer le BrochureUpload contrôle FileUpload à partir des modèles DetailsView. Ensuite, si une brochure a été chargée, l’extension du fichier chargé est examinée. Si l’extension n’est pas .PDF, un avertissement s’affiche, l’insertion est annulée et l’exécution du gestionnaire d’événements se termine.

Notes

Le fait de s’appuyer sur l’extension du fichier chargé n’est pas une technique sûre pour garantir que le fichier chargé est un document PDF. L’utilisateur peut avoir un document PDF valide avec l’extension .Brochure, ou peut avoir pris un document non-PDF et lui avoir donné une .pdf extension. Le contenu binaire du fichier doit être examiné par programmation afin de vérifier de manière plus concluante le type de fichier. Cependant, ces approches approfondies sont souvent excessives ; la vérification de l’extension est suffisante pour la plupart des scénarios.

Comme indiqué dans le didacticiel Chargement de fichiers , vous devez prendre soin de l’enregistrement des fichiers dans le système de fichiers afin que le chargement d’un utilisateur ne remplace pas les autres fichiers. Pour ce tutoriel, nous allons essayer d’utiliser le même nom que le fichier chargé. S’il existe déjà un fichier dans le ~/Brochures répertoire portant ce même nom de fichier, nous ajouterons un nombre à la fin jusqu’à ce qu’un nom unique soit trouvé. Par exemple, si l’utilisateur charge un fichier de brochure nommé Meats.pdf, mais qu’il existe déjà un fichier nommé Meats.pdf dans le ~/Brochures dossier, nous changeons le nom du fichier enregistré en Meats-1.pdf. Si cela existe, nous allons essayer Meats-2.pdf, et ainsi de suite, jusqu’à ce qu’un nom de fichier unique soit trouvé.

Le code suivant utilise la File.Exists(path) méthode pour déterminer si un fichier existe déjà avec le nom de fichier spécifié. Si c’est le cas, il continue d’essayer de nouveaux noms de fichiers pour la brochure jusqu’à ce qu’aucun conflit ne soit trouvé.

const string BrochureDirectory = "~/Brochures/";
string brochurePath = BrochureDirectory + BrochureUpload.FileName;
string fileNameWithoutExtension = 
    System.IO.Path.GetFileNameWithoutExtension(BrochureUpload.FileName);
int iteration = 1;
while (System.IO.File.Exists(Server.MapPath(brochurePath)))
{
    brochurePath = string.Concat(BrochureDirectory, 
        fileNameWithoutExtension, "-", iteration, ".pdf");
    iteration++;
}

Une fois qu’un nom de fichier valide a été trouvé, le fichier doit être enregistré dans le système de fichiers et la valeur de brochurePath``InsertParameter ObjectDataSource doit être mise à jour afin que ce nom de fichier soit écrit dans la base de données. Comme nous l’avons vu dans le didacticiel Chargement de fichiers , le fichier peut être enregistré à l’aide de la méthode s du SaveAs(path) contrôle FileUpload. Pour mettre à jour le paramètre ObjectDataSource, brochurePath utilisez la e.Values collection .

// Save the file to disk and set the value of the brochurePath parameter
BrochureUpload.SaveAs(Server.MapPath(brochurePath));
e.Values["brochurePath"] = brochurePath;

Étape 7 : Enregistrement de l’image chargée dans la base de données

Pour stocker l’image chargée dans le nouvel Categories enregistrement, nous devons affecter le contenu binaire chargé au paramètre ObjectDataSource dans picture l’événement ItemInserting DetailsView. Toutefois, avant d’effectuer cette affectation, nous devons d’abord nous assurer que l’image chargée est une image JPG et non un autre type d’image. Comme à l’étape 6, utilisons l’extension de fichier de l’image chargée pour déterminer son type.

Bien que la Categories table autorise NULL les valeurs pour la Picture colonne, toutes les catégories ont actuellement une image. Forcez l’utilisateur à fournir une image lors de l’ajout d’une nouvelle catégorie via cette page. Le code suivant vérifie qu’une image a été chargée et qu’elle a une extension appropriée.

// Reference the FileUpload controls
FileUpload PictureUpload = (FileUpload)NewCategory.FindControl("PictureUpload");
if (PictureUpload.HasFile)
{
    // Make sure that a JPG has been uploaded
    if (string.Compare(System.IO.Path.GetExtension(PictureUpload.FileName), 
            ".jpg", true) != 0 &&
        string.Compare(System.IO.Path.GetExtension(PictureUpload.FileName), 
            ".jpeg", true) != 0)
    {
        UploadWarning.Text = 
            "Only JPG documents may be used for a category's picture.";
        UploadWarning.Visible = true;
        e.Cancel = true;
        return;
    }
}
else
{
    // No picture uploaded!
    UploadWarning.Text = 
        "You must provide a picture for the new category.";
    UploadWarning.Visible = true;
    e.Cancel = true;
    return;
}

Ce code doit être placé avant le code de l’étape 6 afin qu’en cas de problème avec le chargement de l’image, le gestionnaire d’événements se termine avant l’enregistrement du fichier de brochure dans le système de fichiers.

En supposant qu’un fichier approprié a été chargé, affectez le contenu binaire chargé à la valeur du paramètre d’image avec la ligne de code suivante :

// Set the value of the picture parameter
e.Values["picture"] = PictureUpload.FileBytes;

Gestionnaire d’événements CompleteItemInserting

Pour l’exhaustivité, voici le ItemInserting gestionnaire d’événements dans son intégralité :

protected void NewCategory_ItemInserting(object sender, DetailsViewInsertEventArgs e)
{
    // Reference the FileUpload controls
    FileUpload PictureUpload = (FileUpload)NewCategory.FindControl("PictureUpload");
    if (PictureUpload.HasFile)
    {
        // Make sure that a JPG has been uploaded
        if (string.Compare(System.IO.Path.GetExtension(PictureUpload.FileName), 
                ".jpg", true) != 0 &&
            string.Compare(System.IO.Path.GetExtension(PictureUpload.FileName), 
                ".jpeg", true) != 0)
        {
            UploadWarning.Text = 
                "Only JPG documents may be used for a category's picture.";
            UploadWarning.Visible = true;
            e.Cancel = true;
            return;
        }
    }
    else
    {
        // No picture uploaded!
        UploadWarning.Text = 
            "You must provide a picture for the new category.";
        UploadWarning.Visible = true;
        e.Cancel = true;
        return;
    }
    // Set the value of the picture parameter
    e.Values["picture"] = PictureUpload.FileBytes;
    
    
    // Reference the FileUpload controls
    FileUpload BrochureUpload = 
        (FileUpload)NewCategory.FindControl("BrochureUpload");
    if (BrochureUpload.HasFile)
    {
        // Make sure that a PDF has been uploaded
        if (string.Compare(System.IO.Path.GetExtension(BrochureUpload.FileName), 
            ".pdf", true) != 0)
        {
            UploadWarning.Text = 
                "Only PDF documents may be used for a category's brochure.";
            UploadWarning.Visible = true;
            e.Cancel = true;
            return;
        }
        const string BrochureDirectory = "~/Brochures/";
        string brochurePath = BrochureDirectory + BrochureUpload.FileName;
        string fileNameWithoutExtension = 
            System.IO.Path.GetFileNameWithoutExtension(BrochureUpload.FileName);
        int iteration = 1;
        while (System.IO.File.Exists(Server.MapPath(brochurePath)))
        {
            brochurePath = string.Concat(BrochureDirectory, fileNameWithoutExtension, 
                "-", iteration, ".pdf");
            iteration++;
        }
        // Save the file to disk and set the value of the brochurePath parameter
        BrochureUpload.SaveAs(Server.MapPath(brochurePath));
        e.Values["brochurePath"] = brochurePath;
    }
}

Étape 8 : Correction de laDisplayCategoryPicture.aspxpage

Prenons un moment pour tester l’interface d’insertion et ItemInserting le gestionnaire d’événements qui ont été créés au cours des dernières étapes. Visitez la UploadInDetailsView.aspx page via un navigateur et essayez d’ajouter une catégorie, mais omettez l’image, ou spécifiez une image non JPG ou une brochure non PDF. Dans l’un de ces cas, un message d’erreur s’affiche et le workflow d’insertion est annulé.

Un message d’avertissement s’affiche si un type de fichier non valide est chargé

Figure 9 : Un message d’avertissement s’affiche si un type de fichier non valide est chargé (cliquez pour afficher l’image en taille réelle)

Une fois que vous avez vérifié que la page nécessite le chargement d’une image et que vous n’acceptez pas les fichiers non-PDF ou non JPG, ajoutez une nouvelle catégorie avec une image JPG valide, en laissant le champ Brochure vide. Après avoir cliqué sur le bouton Insérer, la page est publiée et un nouvel enregistrement est ajouté à la Categories table avec le contenu binaire de l’image chargée stocké directement dans la base de données. GridView est mis à jour et affiche une ligne pour la catégorie nouvellement ajoutée, mais, comme le montre la figure 10, l’image de la nouvelle catégorie n’est pas affichée correctement.

L’image de la nouvelle catégorie n’est pas affichée

Figure 10 : L’image de la nouvelle catégorie n’est pas affichée (Cliquez pour afficher l’image en taille réelle)

La raison pour laquelle la nouvelle image n’est pas affichée est due au fait que la DisplayCategoryPicture.aspx page qui retourne une image de catégorie spécifiée est configurée pour traiter les bitmaps qui ont un en-tête OLE. Cet en-tête de 78 octets est supprimé du contenu binaire de la Picture colonne avant d’être renvoyé au client. Mais le fichier JPG que nous venons de charger pour la nouvelle catégorie n’a pas cet en-tête OLE ; Par conséquent, les octets nécessaires et valides sont supprimés des données binaires de l’image.

Étant donné qu’il existe désormais des bitmaps avec des en-têtes OLE et des JPG dans la Categories table, nous devons effectuer la mise à jour DisplayCategoryPicture.aspx afin qu’elle effectue le stripping d’en-tête OLE pour les huit catégories d’origine et contourne cette suppression pour les enregistrements de catégorie plus récents. Dans notre prochain tutoriel, nous allons examiner comment mettre à jour une image d’enregistrement existante, et nous allons mettre à jour toutes les anciennes images de catégorie afin qu’elles soient des JPG. Pour l’instant, cependant, utilisez le code suivant dans DisplayCategoryPicture.aspx pour supprimer les en-têtes OLE uniquement pour ces huit catégories d’origine :

protected void Page_Load(object sender, EventArgs e)
{
    int categoryID = Convert.ToInt32(Request.QueryString["CategoryID"]);
    // Get information about the specified category
    CategoriesBLL categoryAPI = new CategoriesBLL();
    Northwind.CategoriesDataTable categories = 
        categoryAPI.GetCategoryWithBinaryDataByCategoryID(categoryID);
    Northwind.CategoriesRow category = categories[0];
    if (categoryID <= 8)
    {
        // For older categories, we must strip the OLE header... images are bitmaps
        // Output HTTP headers providing information about the binary data
        Response.ContentType = "image/bmp";
        // Output the binary data
        // But first we need to strip out the OLE header
        const int OleHeaderLength = 78;
        int strippedImageLength = category.Picture.Length - OleHeaderLength;
        byte[] strippedImageData = new byte[strippedImageLength];
        Array.Copy(category.Picture, OleHeaderLength, strippedImageData, 
            0, strippedImageLength);
        Response.BinaryWrite(strippedImageData);
    }
    else
    {
        // For new categories, images are JPGs...
        
        // Output HTTP headers providing information about the binary data
        Response.ContentType = "image/jpeg";
        // Output the binary data
        Response.BinaryWrite(category.Picture);
    }
}

Avec cette modification, l’image JPG est désormais restituée correctement dans GridView.

Les images JPG pour les nouvelles catégories sont correctement rendues

Figure 11 : Les images JPG pour les nouvelles catégories sont correctement rendues (cliquer pour afficher l’image en taille réelle)

Étape 9 : Suppression de la brochure dans le visage d’une exception

L’un des défis du stockage des données binaires sur le système de fichiers du serveur web est qu’il introduit une déconnexion entre le modèle de données et ses données binaires. Par conséquent, chaque fois qu’un enregistrement est supprimé, les données binaires correspondantes sur le système de fichiers doivent également être supprimées. Cela peut également entrer en jeu lors de l’insertion. Considérez le scénario suivant : un utilisateur ajoute une nouvelle catégorie, en spécifiant une image et une brochure valides. Lorsque vous cliquez sur le bouton Insérer, une publication se produit et l’événement DetailsView se ItemInserting déclenche, ce qui enregistre la brochure dans le système de fichiers du serveur web. Ensuite, la méthode ObjectDataSource est Insert() appelée, qui appelle la méthode s InsertWithPicture de la CategoriesBLL classe, qui appelle la CategoriesTableAdapter méthode sInsertWithPicture.

Maintenant, que se passe-t-il si la base de données est hors connexion ou s’il y a une erreur dans l’instruction INSERT SQL ? Il est clair que l’insertion échoue, de sorte qu’aucune nouvelle ligne de catégorie n’est ajoutée à la base de données. Mais nous avons toujours le fichier de brochure chargé se trouve sur le système de fichiers du serveur web ! Ce fichier doit être supprimé en face d’une exception pendant le flux de travail d’insertion.

Comme indiqué précédemment dans le didacticiel Gestion des exceptions BLL- et DAL-Level dans un ASP.NET Page , lorsqu’une exception est levée à partir des profondeurs de l’architecture, elle est mise en bulles à travers les différentes couches. Au niveau de la couche de présentation, nous pouvons déterminer si une exception s’est produite à partir de l’événement DetailsView.ItemInserted Ce gestionnaire d’événements fournit également les valeurs de ObjectDataSource s InsertParameters. Par conséquent, nous pouvons créer un gestionnaire d’événements pour l’événement ItemInserted qui vérifie s’il existe une exception et, le cas échéant, supprime le fichier spécifié par le paramètre s brochurePath ObjectDataSource :

protected void NewCategory_ItemInserted
    (object sender, DetailsViewInsertedEventArgs e)
{
    if (e.Exception != null)
    {
        // Need to delete brochure file, if it exists
        if (e.Values["brochurePath"] != null)
            System.IO.File.Delete(Server.MapPath(
                e.Values["brochurePath"].ToString()));
    }
}

Résumé

Un certain nombre d’étapes doivent être effectuées afin de fournir une interface web permettant d’ajouter des enregistrements qui incluent des données binaires. Si les données binaires sont stockées directement dans la base de données, il est probable que vous deviez mettre à jour l’architecture, en ajoutant des méthodes spécifiques pour gérer le cas où les données binaires sont insérées. Une fois l’architecture mise à jour, l’étape suivante consiste à créer l’interface d’insertion, qui peut être effectuée à l’aide d’un DetailsView qui a été personnalisé pour inclure un contrôle FileUpload pour chaque champ de données binaires. Les données chargées peuvent ensuite être enregistrées dans le système de fichiers du serveur web ou affectées à un paramètre de source de données dans le gestionnaire d’événements DetailsView.ItemInserting

L’enregistrement de données binaires dans le système de fichiers nécessite plus de planification que l’enregistrement de données directement dans la base de données. Un schéma de nommage doit être choisi afin d’éviter qu’un chargement d’un utilisateur ne remplace un autre s. En outre, des étapes supplémentaires doivent être effectuées pour supprimer le fichier chargé si l’insertion de base de données échoue.

Nous avons maintenant la possibilité d’ajouter de nouvelles catégories au système avec une brochure et une image, mais nous n’avons pas encore examiné comment mettre à jour les données binaires d’une catégorie existante ou comment supprimer correctement les données binaires d’une catégorie supprimée. Nous allons explorer ces deux rubriques dans le tutoriel suivant.

Bonne programmation !

À propos de l’auteur

Scott Mitchell, auteur de sept livres ASP/ASP.NET et fondateur de 4GuysFromRolla.com, travaille avec les technologies Web Microsoft depuis 1998. Scott travaille comme consultant indépendant, formateur et écrivain. Son dernier livre est Sams Teach Yourself ASP.NET 2.0 in 24 Heures. Il est accessible à l’adressemitchell@4GuysFromRolla.com . ou via son blog, qui peut être trouvé à l’adresse http://ScottOnWriting.NET.

Un merci spécial à

Cette série de tutoriels a été examinée par de nombreux réviseurs utiles. Les principaux réviseurs de ce tutoriel étaient Dave Gardner, Teresa Murphy et Bernadette Leigh. Vous souhaitez consulter mes prochains articles MSDN ? Si c’est le cas, déposez-moi une ligne à mitchell@4GuysFromRolla.com.