Udostępnij za pośrednictwem


Dołączanie opcji przekazywania pliku podczas dodawania nowego rekordu (C#)

Autor : Scott Mitchell

Pobierz plik PDF

W tym samouczku pokazano, jak utworzyć interfejs internetowy, który umożliwia użytkownikowi wprowadzanie danych tekstowych i przekazywanie plików binarnych. Aby zilustrować dostępne opcje przechowywania danych binarnych, jeden plik zostanie zapisany w bazie danych, podczas gdy drugi jest przechowywany w systemie plików.

Wprowadzenie

W dwóch poprzednich samouczkach omówiliśmy techniki przechowywania danych binarnych skojarzonych z modelem danych aplikacji, przyjrzeliśmy się sposobom wysyłania plików z klienta do serwera internetowego za pomocą kontrolki FileUpload. Przedstawiono również sposób prezentowania tych danych binarnych w kontrolce sieci Web danych. Jeszcze nie omówiliśmy, jak kojarzyć przekazane dane z modelem danych.

W tym samouczku utworzymy stronę WWW do dodania nowej kategorii. Oprócz pola TextBoxes dla nazwy i opisu kategorii ta strona musi zawierać dwie kontrolki FileUpload jeden dla nowego obrazu kategorii i jeden dla broszury. Przekazany obraz będzie przechowywany bezpośrednio w nowej kolumnie rekordu Picture , natomiast broszura zostanie zapisana w ~/Brochures folderze ze ścieżką do pliku zapisanego w nowej kolumnie rekordu BrochurePath .

Przed utworzeniem nowej strony internetowej musimy zaktualizować architekturę. Główne CategoriesTableAdapter zapytanie nie pobiera kolumny Picture . Automatycznie wygenerowana metoda Insert zawiera tylko dane wejściowe dla pól CategoryName, Description i BrochurePath. W związku z tym musimy utworzyć dodatkową metodę w TableAdapterze, która wyświetla monit o wszystkie cztery Categories pola. Klasę CategoriesBLL w warstwie logiki biznesowej również należy zaktualizować.

Krok 1. DodawanieInsertWithPicturemetody do metodyCategoriesTableAdapter

Podczas tworzenia CategoriesTableAdapter w samouczku Tworzenie warstwy dostępu do danych, skonfigurowaliśmy ją do automatycznego generowania instrukcji INSERT, UPDATE i DELETE na podstawie głównego zapytania. Ponadto poinstruowaliśmy metodę TableAdapter, aby stosować metodę DB Direct, która utworzyła metody Insert, Updatei Delete. Te metody wykonują automatycznie generowane instrukcje INSERT, UPDATE i DELETE, a w związku z tym akceptują parametry wejściowe na podstawie kolumn wynikowych głównego zapytania. W samouczku Przekazywanie plików rozbudowaliśmy główne zapytanie, aby użyć kolumny CategoriesTableAdapter.

CategoriesTableAdapter Ponieważ główne zapytanie nie odwołuje się do Picture kolumny, nie możemy ani dodać nowego rekordu, ani zaktualizować istniejącego rekordu Picture z wartością dla kolumny. Aby przechwycić te informacje, możemy utworzyć nową metodę w tabeli TableAdapter, która jest używana specjalnie do wstawiania rekordu z danymi binarnymi lub możemy dostosować instrukcję wygenerowaną INSERT automatycznie. Problem z dostosowywaniem instrukcji generowanej INSERT automatycznie polega na tym, że istnieje ryzyko, że nasze dostosowania zostaną zastąpione przez kreatora. Załóżmy na przykład, że dostosujemy instrukcję INSERT tak, aby zawierała użycie kolumny Picture . Spowoduje to zaktualizowanie metody TableAdapter Insert w celu uwzględnienia dodatkowego parametru wejściowego dla binarnych danych obrazu kategorii. Następnie można utworzyć metodę w warstwie logiki biznesowej, aby użyć tej metody DAL i wywołać tę metodę BLL za pośrednictwem warstwy prezentacji, a wszystko działałoby cudownie. To znaczy, że będzie tak, dopóki ponownie nie skonfigurujemy TableAdaptera za pomocą kreatora konfiguracji TableAdapter. Po zakończeniu pracy kreatora nasze dostosowania do instrukcji INSERT zostaną zastąpione, metoda Insert powróci do starej formy, a nasz kod nie będzie już się kompilował.

Uwaga / Notatka

To irytowanie nie jest problemem podczas korzystania z procedur składowanych zamiast instrukcji SQL ad hoc. W przyszłym samouczku będzie omawiane używanie procedur składowanych zamiast ad hoc instrukcji SQL w warstwie dostępu do danych.

Aby uniknąć tego potencjalnego bólu głowy, zamiast dostosowywać automatycznie generowane instrukcje SQL, zamiast tego utwórz nową metodę dla tabeli TableAdapter. Ta metoda o nazwie InsertWithPicturebędzie akceptować wartości dla CategoryNamekolumn , Description, BrochurePathi Picture i wykonuje instrukcję INSERT , która przechowuje wszystkie cztery wartości w nowym rekordzie.

Otwórz sparametryzowany zestaw danych i w projektancie kliknij prawym przyciskiem myszy nagłówek CategoriesTableAdapter i wybierz polecenie Dodaj zapytanie z menu kontekstowego. Spowoduje to uruchomienie Kreatora konfiguracji zapytań TableAdapter, który zaczyna się od pytania, w jaki sposób zapytanie TableAdapter powinno uzyskać dostęp do bazy danych. Wybierz pozycję Użyj instrukcji SQL i kliknij przycisk Dalej. W następnym kroku zostanie wyświetlony monit o wygenerowanie typu zapytania. Ponieważ tworzysz zapytanie w celu dodania nowego rekordu Categories do tabeli, wybierz pozycję WSTAW i kliknij przycisk Dalej.

Wybierz opcję INSERT

Rysunek 1. Wybierz opcję INSERT (Kliknij, aby wyświetlić obraz o pełnym rozmiarze)

Teraz musimy określić instrukcję INSERT SQL. Kreator automatycznie sugeruje instrukcję odpowiadającą INSERT do głównego zapytania TableAdapter. W tym przypadku jest to instrukcja, która wstawia wartości INSERT, CategoryName i Description. Zaktualizuj instrukcję tak, aby kolumna Picture została dołączona wraz z parametrem @Picture w następujący sposób:

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

Końcowy ekran kreatora prosi nas o nadanie nazwy nowej metodzie TableAdapter. Wprowadź InsertWithPicture i kliknij przycisk Zakończ.

Nadaj nowej metodzie TableAdapter nazwę InsertWithPicture

Rysunek 2. Nadaj nowej metodzie InsertWithPicture TableAdapter nazwę (kliknij, aby wyświetlić obraz pełnowymiarowy)

Krok 2. Aktualizowanie warstwy logiki biznesowej

Ponieważ warstwa prezentacji powinna być interfejsem tylko z warstwą logiki biznesowej, a nie pomijając ją, aby przejść bezpośrednio do warstwy dostępu do danych, musimy utworzyć metodę BLL, która wywołuje właśnie utworzoną metodę DAL (InsertWithPicture). Na potrzeby tego samouczka utwórz metodę w CategoriesBLL klasie o nazwie InsertWithPicture , która przyjmuje jako dane wejściowe trzy string s i tablicę byte . Parametry wejściowe string są przeznaczone dla nazwy kategorii, opisu oraz ścieżki pliku broszury, natomiast tablica byte dotyczy binarnej zawartości zdjęcia kategorii. Jak pokazano w poniższym kodzie, ta metoda BLL wywołuje odpowiednią metodę DAL:

[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);
}

Uwaga / Notatka

Przed dodaniem InsertWithPicture metody do biblioteki BLL upewnij się, że zapisano typowy zestaw danych. CategoriesTableAdapter Ponieważ kod klasy jest generowany automatycznie na podstawie typowanego zestawu danych, jeśli nie zapiszesz najpierw zmian w tym typowanym zestawie danych, właściwość Adapter nie będzie wiedziała o metodzie InsertWithPicture.

Krok 3. Wyświetlanie listy istniejących kategorii i ich danych binarnych

W tym samouczku utworzymy stronę umożliwiającą użytkownikowi końcowemu dodanie nowej kategorii do systemu, udostępnienie obrazu i broszury dla nowej kategorii. W poprzednim samouczku użyliśmy kontrolki GridView z polami TemplateField i ImageField, aby wyświetlić nazwę kategorii, jej opis, zdjęcie oraz link do pobrania broszury dotyczącej każdej kategorii. Zreplikujmy tę funkcjonalność tego samouczka, tworząc stronę zawierającą listę wszystkich istniejących kategorii i umożliwiającą utworzenie nowych.

Rozpocznij od otwarcia DisplayOrDownload.aspx strony z BinaryData folderu . Przejdź do widoku Źródło i skopiuj składnię deklaracyjną GridView i ObjectDataSource, wklejając ją w elemencie <asp:Content> w elemencie UploadInDetailsView.aspx. Ponadto nie zapomnij skopiować metody GenerateBrochureLink z klasy DisplayOrDownload.aspx do klasy UploadInDetailsView.aspx.

Skopiuj i wklej składnię deklaratywną z DisplayOrDownload.aspx do UploadInDetailsView.aspx

Rysunek 3. Kopiowanie i wklejanie składni deklaratywnej z DisplayOrDownload.aspx do UploadInDetailsView.aspx (kliknij, aby wyświetlić obraz o pełnym rozmiarze)

Po skopiowaniu składni deklaratywnej i GenerateBrochureLink metody na UploadInDetailsView.aspx stronę wyświetl stronę za pośrednictwem przeglądarki, aby upewnić się, że wszystko zostało skopiowane poprawnie. Powinna zostać wyświetlona lista GridView zawierająca osiem kategorii, które zawierają link do pobrania broszury, a także obrazu kategorii.

Powinna być teraz widoczna każda kategoria wraz z danymi binarnymi

Rysunek 4. Powinna być teraz widoczna każda kategoria wraz z danymi binarnymi (kliknij, aby wyświetlić obraz o pełnym rozmiarze)

Krok 4. Konfigurowanie CategoriesDataSource do obsługi wstawiania

Obiekt CategoriesDataSource ObjectDataSource używany przez Categories GridView nie zapewnia możliwości wstawiania danych. Aby umożliwić obsługę wstawiania za pomocą tej kontrolki źródła danych, musimy zamapować jej metodę Insert na odpowiednią metodę w jej obiekcie bazowym CategoriesBLL. W szczególności chcemy ją zamapować na metodę CategoriesBLL, którą dodaliśmy ponownie w kroku 2. InsertWithPicture

Zacznij od kliknięcia łącza Konfiguruj źródło danych w inteligentnym tagu ObjectDataSource. Pierwszy ekran przedstawia obiekt skonfigurowany do pracy ze źródłem danych. CategoriesBLL Pozostaw to ustawienie as-is i kliknij przycisk Dalej, aby przejść do ekranu Definiowanie metod danych. Przejdź do karty INSERT i wybierz InsertWithPicture metodę z listy rozwijanej. Kliknij przycisk Zakończ, aby ukończyć pracę kreatora.

Konfigurowanie obiektu ObjectDataSource do używania metody InsertWithPicture

Rysunek 5. Konfigurowanie obiektu ObjectDataSource do użycia InsertWithPicture metody (kliknij, aby wyświetlić obraz o pełnym rozmiarze)

Uwaga / Notatka

Po ukończeniu pracy kreatora program Visual Studio może zapytać, czy chcesz odświeżyć pola i klucze, co spowoduje ponowne wygenerowanie pól kontrolek sieci Web danych. Wybierz pozycję Nie, ponieważ wybranie pozycji Tak spowoduje zastąpienie wszelkich dostosowań pól, które mogły zostać wprowadzone.

Po zakończeniu pracy kreatora obiekt ObjectDataSource będzie teraz zawierać wartość jego InsertMethod właściwości, a także InsertParameters dla czterech kolumn kategorii, jak ilustruje następujący znacznik deklaratywny:

<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>

Krok 5. Tworzenie interfejsu wstawiania

Omówiony po raz pierwszy w Przeglądzie wstawiania, aktualizowania i usuwania danych, kontrolka DetailsView udostępnia wbudowany interfejs wstawiania, który można wykorzystać podczas pracy z kontrolką źródła danych obsługującą wstawianie. Dodajmy kontrolkę DetailsView na tej stronie nad kontrolką GridView, który będzie na stałe wyświetlał swój interfejs dodawania, umożliwiając użytkownikowi szybkie dodanie nowej kategorii. Po dodaniu nowej kategorii w widoku DetailsView, element GridView pod nim zostanie automatycznie odświeżony i wyświetli nową kategorię.

Zacznij od przeciągnięcia kontrolki DetailsView z przybornika do projektanta nad obiektem GridView, ustaw jej właściwość na ID oraz wyczyść wartości właściwości NewCategory i Height. Z inteligentnego znacznika DetailsView powiąż ją z istniejącą CategoriesDataSource, a następnie zaznacz pole wyboru "Włącz wstawianie".

Zrzut ekranu przedstawiający widok DetailsView otwarty z właściwością CategoryID ustawioną na wartość NewCategory, puste wartości właściwości Height i Width oraz zaznaczone pole wyboru Włącz wstawianie.

Rysunek 6. Powiązanie kontrolki DetailsView z kontrolką CategoriesDataSource i Włączanie wstawiania (kliknij, aby wyświetlić obraz o pełnym rozmiarze)

Aby trwale renderować element DetailsView w interfejsie wstawiania, ustaw jego właściwość DefaultMode na Insert.

Należy pamiętać, że element DetailsView ma pięć pól BoundField CategoryID, CategoryName, Description, NumberOfProducts i BrochurePath, mimo że pole CategoryID BoundField nie jest renderowane w interfejsie wstawiania, ponieważ jego właściwość InsertVisible jest ustawiona na false. Te BoundFields istnieją, ponieważ są to kolumny zwracane przez metodę GetCategories(), którą obiekt ObjectDataSource wywołuje w celu pobrania swoich danych. W przypadku wstawiania nie chcemy jednak zezwalać użytkownikowi na określenie wartości dla NumberOfProducts. Ponadto musimy zezwolić im na przekazanie obrazu dla nowej kategorii, a także przekazać plik PDF do broszury.

Całkowicie usuń pole NumberOfProducts BoundField z widoku DetailsView, a następnie zaktualizuj właściwości HeaderText elementów CategoryName i BrochurePath BoundFields odpowiednio do "Kategorii" i "Broszury". Następnie przekonwertuj BrochurePath pole BoundField na pole szablonu i dodaj nowe pole szablonu dla obrazu, dając temu nowemu HeaderText polu TemplateField wartość obrazu. Przenieś Picture pole TemplateField, aby było między BrochurePath polem TemplateField a polem CommandField.

Zrzut ekranu okna pól z wyróżnionymi polami TemplateField, Picture i HeaderText.

Rysunek 7. Powiąż kontrolkę DetailsView z kontrolką CategoriesDataSource i włącz wstawianie

Jeśli przekonwertowano BrochurePath pole BoundField na pole TemplateField za pomocą okna dialogowego Edytowanie pól, pole TemplateField zawiera ItemTemplate, EditItemTemplate i InsertItemTemplate. InsertItemTemplate Potrzebne są jednak tylko te elementy, więc możesz usunąć pozostałe dwa szablony. W tym momencie składnia deklaratywna kontrolki DetailsView powinna wyglądać następująco:

<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>

Dodawanie kontrolek FileUpload dla pól broszury i obrazu

Obecnie pole BrochurePath TemplateField InsertItemTemplate zawiera pole tekstowe, a Picture pole szablonu nie zawiera żadnych szablonów. Aby użyć kontrolek FileUpload, należy zaktualizować te dwa obiekty TemplateField s InsertItemTemplate .

W inteligentnym tagu DetailsView wybierz opcję „Edytuj szablony”, a następnie wybierz TemplateField z listy rozwijanej. Usuń kontrolkę TextBox, a następnie przeciągnij kontrolkę FileUpload z przybornika do szablonu. Ustaw kontrolkę FileUpload s ID na BrochureUpload. Podobnie dodaj kontrolkę FileUpload do elementu Picture TemplateField s InsertItemTemplate. Ustaw tę kontrolkę FileUpload s ID na PictureUpload.

Dodawanie kontrolki FileUpload do elementu InsertItemTemplate

Rysunek 8. Dodawanie kontrolki FileUpload do elementu InsertItemTemplate (kliknij, aby wyświetlić obraz o pełnym rozmiarze)

Po dodaniu tych dodatków dwie składnie deklaratywne templateField będą następujące:

<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>

Gdy użytkownik dodaje nową kategorię, chcemy mieć pewność, że broszura i obraz mają prawidłowy typ pliku. W przypadku broszury użytkownik musi podać plik PDF. W przypadku obrazu potrzebujemy użytkownika do przekazania pliku obrazu, ale czy zezwalamy na dowolny plik obrazu lub tylko pliki obrazów określonego typu, takie jak pliki GIF lub JPG? Aby umożliwić korzystanie z różnych typów plików, należy rozszerzyć schemat Categories tak, aby zawierał kolumnę, która zawiera informacje o typie pliku, aby ten typ mógł być wysłany do klienta za pomocą polecenia Response.ContentType w programie DisplayCategoryPicture.aspx. Ponieważ nie mamy takiej kolumny, rozsądnie byłoby ograniczyć użytkowników tylko do udostępniania określonego typu pliku obrazu. Istniejące obrazy w tabeli to mapy bitowe, ale pliki JPG są bardziej odpowiednim formatem plików dla obrazów dostarczanych przez Internet.

Jeśli użytkownik przekaże nieprawidłowy typ pliku, musimy anulować wstawianie i wyświetlić komunikat wskazujący problem. Dodaj kontrolkę typu Label dla aplikacji internetowej pod kontrolką DetailsView. Ustaw jego ID właściwość na UploadWarning, wyczyść jego Text właściwość, ustaw właściwość CssClass na Ostrzeżenie, a właściwości Visible i EnableViewState na false. Klasa Warning CSS jest zdefiniowana w Styles.css i renderuje tekst dużą, czerwoną, pogrubioną, kursywą czcionką.

Uwaga / Notatka

Idealnie obiekty CategoryName i Description BoundFields powinny zostać przekonwertowane na pola TemplateFields, a ich interfejsy wstawiania dostosowane. Na przykład, interfejs wstawiający prawdopodobnie lepiej się sprawdzi w przypadku wielowierszowego pola tekstowego. Ponieważ kolumna CategoryName nie akceptuje NULL wartości, należy dodać element RequiredFieldValidator, aby upewnić się, że użytkownik podaje wartość dla nowej nazwy kategorii. Te kroki są pozostawione jako ćwiczenie dla czytelnika. Wróć do tematu Dostosowywanie interfejsu modyfikacji danych , aby uzyskać szczegółowe informacje na temat rozszerzania interfejsów modyfikacji danych.

Krok 6. Zapisanie przekazanej broszury w systemie plików serwera sieci Web

Gdy użytkownik wprowadzi wartości nowej kategorii i kliknie przycisk Wstaw, nastąpi odświeżenie strony i rozwija się proces wstawiania. Najpierw zdarzenie DetailsView jest inicjowaneItemInserting. Następnie, metoda Insert() ObjectDataSource jest wywoływana, co skutkuje dodaniem nowego rekordu do tabeli Categories. Następnie wyzwala się ItemInserted zdarzenie DetailsView.

Przed wywołaniem metody ObjectDataSource Insert() należy najpierw upewnić się, że odpowiednie typy plików zostały przekazane przez użytkownika, a następnie zapisać broszurę PDF w systemie plików serwera internetowego. Utwórz obsługę zdarzeń dla zdarzenia DetailsView i następnie dodaj następujący kod:

// 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;
    }
}

Procedura obsługi zdarzeń rozpoczyna się od odwołania się do kontrolki BrochureUpload FileUpload z szablonów DetailsView. Następnie, jeśli broszura została przesłana, rozszerzenie przesłanego pliku zostanie zbadane. Jeśli rozszerzenie nie jest .PDF, zostanie wyświetlone ostrzeżenie, wstawianie zostanie anulowane, a wykonanie programu obsługi zdarzeń kończy się.

Uwaga / Notatka

Poleganie na rozszerzeniu przekazanego pliku nie jest techniką zapewnienia, że przekazany plik jest dokumentem PDF. Użytkownik może mieć prawidłowy dokument PDF z rozszerzeniem .Brochurelub mógł pobrać dokument inny niż PDF i nadać mu .pdf rozszerzenie. Zawartość binarna pliku musiałaby zostać zbadana programowo, aby bardziej jednoznacznie zweryfikować typ pliku. Takie dokładne podejścia są jednak często przesadne; sprawdzanie rozszerzenia jest wystarczające w przypadku większości scenariuszy.

Zgodnie z opisem w samouczku Przekazywanie plików, należy zachować ostrożność podczas zapisywania plików w systemie plików, aby przekazanie pliku przez jednego użytkownika nie zastępowało przekazywania pliku przez innego użytkownika. W tym samouczku podejmiemy próbę użycia tej samej nazwy co przekazany plik. Jeśli w katalogu istnieje już plik ~/Brochures o tej samej nazwie pliku, jednak dołączymy liczbę na końcu do momentu znalezienia unikatowej nazwy. Jeśli na przykład użytkownik przekaże plik broszury o nazwie Meats.pdf, ale istnieje już plik o nazwie Meats.pdf w folderze ~/Brochures, zmienimy zapisaną nazwę pliku na Meats-1.pdf. Jeśli tak istnieje, spróbujemy wykonać polecenie Meats-2.pdfi tak dalej, dopóki nie zostanie znaleziona unikatowa nazwa pliku.

Poniższy kod używa File.Exists(path) metody , aby określić, czy plik już istnieje z określoną nazwą pliku. Jeśli tak, nadal próbuje nowych nazw plików dla broszury, aż do napotkania braku konfliktu.

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++;
}

Po znalezieniu prawidłowej nazwy pliku należy zapisać plik w systemie plików, a wartość objectDataSource brochurePath``InsertParameter musi zostać zaktualizowana, aby ta nazwa pliku została zapisana w bazie danych. Jak widzieliśmy już w samouczku Przekazywanie plików, plik można zapisać przy użyciu metody kontrolki FileUpload SaveAs(path). Aby zaktualizować parametr objectDataSource s brochurePath , użyj kolekcji e.Values .

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

Krok 7. Zapisywanie przekazanego obrazu w bazie danych

Aby zapisać przekazany obraz w nowym Categories rekordzie, musimy przypisać przekazaną zawartość binarną do parametru ObjectDataSource picture w zdarzeniu DetailsView ItemInserting . Zanim jednak wykonamy to przypisanie, musimy najpierw upewnić się, że przekazany obraz jest obrazem JPG, a nie innym typem obrazu. Tak jak w kroku 6, użyjmy przekazanego rozszerzenia pliku obrazu, aby ustalić jego typ.

Categories Chociaż tabela zezwala na NULL wartości dla Picture kolumny, wszystkie kategorie mają obecnie obraz. Wymuśmy użytkownikowi podanie obrazu podczas dodawania nowej kategorii za pośrednictwem tej strony. Poniższy kod sprawdza, czy obraz został załadowany i czy ma odpowiednie rozszerzenie.

// 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;
}

Ten kod należy umieścić przed kodem z kroku 6, tak aby w przypadku wystąpienia problemu z przekazywaniem obrazu program obsługi zdarzeń zakończył się przed zapisaniem pliku broszury w systemie plików.

Zakładając, że został przekazany odpowiedni plik, przypisz przekazaną zawartość binarną do wartości parametru obrazu przy użyciu następującego wiersza kodu:

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

KompletnaItemInsertingobsługa zdarzeń

Aby uzyskać kompletność, oto ItemInserting procedura obsługi zdarzeń w całości:

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;
    }
}

Krok 8. NaprawianieDisplayCategoryPicture.aspxstrony

Pośmińmy chwilę na przetestowanie interfejsu wstawiania i ItemInserting procedury obsługi zdarzeń utworzonej w ciągu ostatnich kilku kroków. UploadInDetailsView.aspx Odwiedź stronę za pośrednictwem przeglądarki i spróbuj dodać kategorię, ale pominąć obraz lub określić obraz inny niż JPG lub broszurę inną niż PDF. W każdym z tych przypadków zostanie wyświetlony komunikat o błędzie i anulowany przepływ pracy wstawiania.

W przypadku przekazania nieprawidłowego typu pliku zostanie wyświetlony komunikat ostrzegawczy

Rysunek 9. W przypadku przekazania nieprawidłowego typu pliku jest wyświetlany komunikat ostrzegawczy (kliknij, aby wyświetlić obraz o pełnym rozmiarze)

Po sprawdzeniu, że strona wymaga załadowania zdjęcia i nie akceptuje plików innych niż PDF oraz JPG, dodaj nową kategorię z prawidłowym obrazem JPG, pozostawiając pole Broszura puste. Po kliknięciu przycisku Wstaw strona odświeży się, a nowy rekord zostanie dodany do Categories tabeli z przekazanym obrazem w postaci binarnej, przechowanym bezpośrednio w bazie danych. Element GridView jest aktualizowany i pokazuje wiersz dla nowo dodanej kategorii, ale jak pokazano na rysunku 10, nowy obraz kategorii nie jest poprawnie renderowany.

Obraz nowej kategorii nie jest wyświetlany

Rysunek 10. Obraz nowej kategorii nie jest wyświetlany (kliknij, aby wyświetlić obraz o pełnym rozmiarze)

Powodem, dla którego nowy obraz nie jest wyświetlany, wynika z tego, że DisplayCategoryPicture.aspx strona zwracająca określony obraz kategorii jest skonfigurowana do przetwarzania map bitowych, które mają nagłówek OLE. Nagłówek o wielkości 78 bajtów jest usuwany z zawartości binarnej kolumny Picture przed jej odesłaniem do klienta. Ale plik JPG, który właśnie przekazaliśmy dla nowej kategorii, nie ma tego nagłówka OLE; dlatego prawidłowe, niezbędne bajty są usuwane z danych binarnych obrazu.

Ponieważ w tabeli teraz znajdują się zarówno mapy bitowe z nagłówkami OLE, jak i pliki JPG Categories, musimy zaktualizować DisplayCategoryPicture.aspx, aby usuwało nagłówki OLE dla oryginalnych ośmiu kategorii i pomijało to usuwanie dla nowszych rekordów kategorii. W następnym samouczku sprawdzimy, jak zaktualizować istniejący obraz rekordu, a wszystkie stare obrazy kategorii zostaną zaktualizowane tak, aby były to pliki JPG. Na razie jednak użyj następującego kodu w DisplayCategoryPicture.aspx pliku , aby usunąć nagłówki OLE tylko dla tych oryginalnych ośmiu kategorii:

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);
    }
}

Dzięki tej zmianie obraz JPG został poprawnie renderowany w GridView.

Obrazy JPG dla nowych kategorii są poprawnie renderowane

Rysunek 11. Obrazy JPG dla nowych kategorii są poprawnie renderowane (kliknij, aby wyświetlić obraz pełnowymiarowy)

Krok 9: Usunięcie broszury w przypadku wystąpienia wyjątku

Jednym z wyzwań związanych z przechowywaniem danych binarnych w systemie plików serwera internetowego jest wprowadzenie rozłączenia między modelem danych a danymi binarnymi. W związku z tym za każdym razem, gdy rekord zostanie usunięty, należy również usunąć odpowiednie dane binarne w systemie plików. Może to również odgrywać rolę podczas wstawiania. Rozważmy następujący scenariusz: użytkownik dodaje nową kategorię, określając prawidłowy obraz i broszurę. Po kliknięciu przycisku Wstaw następuje postback, a zdarzenie DetailsView ItemInserting zostaje wyzwolone, zapisując broszurę w systemie plików serwera internetowego. Następnie wywoływana jest metoda Insert() obiektu ObjectDataSource, która wywołuje metodę CategoriesBLL klasy InsertWithPicture, a ta z kolei wywołuje metodę CategoriesTableAdapter obiektu InsertWithPicture.

Teraz co się stanie, jeśli baza danych jest w trybie offline lub jeśli wystąpi błąd w instrukcji INSERT SQL? Oczywiście wstawianie zakończy się niepowodzeniem, więc do bazy danych nie zostanie dodany żaden nowy wiersz kategorii. Ale nadal mamy przekazany plik broszury znajdujący się w systemie plików serwera internetowego! Ten plik musi zostać usunięty, gdy wystąpi wyjątek podczas procesu wstawiania.

Jak wspomniano wcześniej w samouczku Obsługa wyjątków BLL i DAL-Level w ASP.NET Page, gdy wyjątek jest zgłaszany z głębi architektury, przechodzi przez różne warstwy. W warstwie prezentacji możemy określić, czy wystąpił wyjątek ze zdarzenia DetailsView ItemInserted . Ta procedura obsługi zdarzeń udostępnia również wartości obiektu ObjectDataSource s InsertParameters. W związku z tym możemy utworzyć program obsługi zdarzeń dla ItemInserted zdarzenia, który sprawdza, czy wystąpił wyjątek, a jeśli tak, usuwa plik określony przez parametr ObjectDataSource s brochurePath :

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()));
    }
}

Podsumowanie

Istnieje wiele kroków, które należy wykonać w celu zapewnienia interfejsu internetowego do dodawania rekordów zawierających dane binarne. Jeśli dane binarne są przechowywane bezpośrednio w bazie danych, prawdopodobnie konieczne będzie zaktualizowanie architektury, dodanie określonych metod do obsługi przypadku wstawiania danych binarnych. Po zaktualizowaniu architektury następnym krokiem jest utworzenie interfejsu wstawiania, który można wykonać przy użyciu kontrolki DetailsView dostosowanej do uwzględnienia kontrolki FileUpload dla każdego pola danych binarnych. Przesłane dane można następnie zapisać w systemie plików serwera internetowego lub przypisać do parametru źródła danych w zdarzeniu DetailsView ItemInserting.

Zapisywanie danych binarnych w systemie plików wymaga więcej planowania niż zapisywanie danych bezpośrednio w bazie danych. Aby uniknąć sytuacji, w której przesyłanie plików przez jednego użytkownika nadpisuje pliki innych użytkowników, należy wybrać odpowiedni schemat nazewnictwa. Ponadto należy wykonać dodatkowe kroki, aby usunąć przekazany plik, jeśli wstawianie bazy danych zakończy się niepowodzeniem.

Obecnie mamy możliwość dodawania nowych kategorii do systemu z broszurą i obrazem, ale jeszcze nie przyjrzeliśmy się, jak zaktualizować dane binarne istniejącej kategorii lub jak poprawnie usunąć dane binarne kategorii, która została usunięta. W następnym samouczku zapoznamy się z tymi dwoma tematami.

Szczęśliwe programowanie!

Informacje o autorze

Scott Mitchell, autor siedmiu książek ASP/ASP.NET i założyciel 4GuysFromRolla.com, współpracuje z technologiami internetowymi firmy Microsoft od 1998 roku. Scott pracuje jako niezależny konsultant, trener i pisarz. Jego najnowsza książka to Sams Teach Yourself ASP.NET 2.0 w ciągu 24 godzin. Można go uzyskać pod adresem mitchell@4GuysFromRolla.com.

Specjalne podziękowania

Ta seria samouczków została omówiona przez wielu przydatnych recenzentów. Głównymi recenzentami tego samouczka byli Dave Gardner, Teresa Murphy i Bernadette Leigh. Chcesz przejrzeć nadchodzące artykuły MSDN? Jeśli tak, napisz do mnie na adres mitchell@4GuysFromRolla.com.