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ć opcje dostępne do 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. Jeszcze nie rozmawialiśmy o tym, jak skojarzyć przekazane dane z modelem danych.

W tym samouczku utworzymy stronę internetową, aby dodać nową kategorię. Oprócz pola TextBoxes dla nazwy i opisu kategorii ta strona będzie musiała 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 . W związku z tym metoda wygenerowana Insert automatycznie ma tylko dane wejściowe dla CategoryNamepól , Descriptioni BrochurePath . W związku z tym musimy utworzyć dodatkową metodę w tableAdapter, która wyświetli monit o podanie wszystkich czterech Categories pól. Należy CategoriesBLL również zaktualizować klasę w warstwie logiki biznesowej.

Krok 1. DodawanieInsertWithPicturemetody do elementuCategoriesTableAdapter

Po utworzeniu CategoriesTableAdapter z powrotem w samouczku Tworzenie warstwy dostępu do danych skonfigurowaliśmy ją do automatycznego generowania INSERTinstrukcji , UPDATEi na DELETE podstawie głównego zapytania. Ponadto poinstruowaliśmy metodę TableAdapter, aby zastosować metodę DB Direct, która utworzyła metody Insert, Updatei Delete. Te metody wykonują automatycznie wygenerowane INSERTinstrukcje , UPDATEi DELETE , w związku z tym akceptują parametry wejściowe na podstawie kolumn zwracanych przez zapytanie główne. W samouczku Przekazywanie plików rozszerzyliśmy CategoriesTableAdapter główne zapytanie, aby użyć kolumny BrochurePath .

CategoriesTableAdapter Ponieważ główne zapytanie nie odwołuje się do Picture kolumny, nie możemy dodać nowego rekordu ani zaktualizować istniejącego rekordu z wartością dla kolumnyPicture. 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 automatycznie wygenerowanej INSERT instrukcji polega na tym, że ryzyko, że nasze dostosowania zostaną zastąpione przez kreatora. Załóżmy na przykład, że dostosowano instrukcję INSERT tak, aby zawierała użycie kolumny Picture . Spowoduje to zaktualizowanie metody TableAdapter w Insert celu uwzględnienia dodatkowego parametru wejściowego dla danych binarnych obrazu kategorii. Następnie możemy 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 będzie działać cudownie. Oznacza to, że do następnego momentu skonfigurowaliśmy narzędzie TableAdapter za pośrednictwem Kreatora konfiguracji tableAdapter. Po zakończeniu działania kreatora nasze dostosowania instrukcji INSERT zostaną zastąpione, Insert metoda powróci do starego formularza, a nasz kod nie będzie już kompilowany.

Uwaga

Ta irytacja jest problemem podczas korzystania z procedur składowanych zamiast instrukcji AD-hoc SQL. W przyszłym samouczku omówiono używanie procedur składowanych zamiast instrukcji AD-hoc SQL w warstwie dostępu do danych.

Aby uniknąć tego potencjalnego bólu głowy, zamiast dostosowywać automatycznie wygenerowane instrukcje SQL, zamiast tego utwórzmy nową metodę dla klasy TableAdapter. Ta metoda o nazwie InsertWithPicture, akceptuje wartości dla CategoryNamekolumn , Description, BrochurePathi Picture wykonuje instrukcję INSERT , która przechowuje wszystkie cztery wartości w nowym rekordzie.

Otwórz typowy zestaw danych i w Projektant kliknij prawym przyciskiem myszy CategoriesTableAdapter nagłówek s, a następnie 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ż tworzymy zapytanie w celu dodania nowego rekordu Categories do tabeli, wybierz pozycję INSERT i kliknij przycisk Dalej.

Wybierz opcję INSERT

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

Teraz musimy określić instrukcję INSERT SQL. Kreator automatycznie sugeruje instrukcję odpowiadającą INSERT głównemu zapytaniu TableAdapter. W tym przypadku jest INSERT to instrukcja, która wstawia CategoryNamewartości , Descriptioni BrochurePath . 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)

Na ostatnim ekranie kreatora zostanie wyświetlony monit o nadenie nazwy nowej metody TableAdapter. Wprowadź InsertWithPicture i kliknij przycisk Zakończ.

Nazwij nową metodę TableAdapter InsertWithPicture

Rysunek 2. Nadaj nazwę nowej metodzie InsertWithPicture TableAdapter (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ą 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 . string Parametry wejściowe są przeznaczone dla nazwy, opisu i ścieżki pliku broszury kategorii, podczas gdy byte tablica jest przeznaczona dla zawartości binarnej obrazu 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

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

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

W tym samouczku utworzymy stronę, która pozwala użytkownikowi końcowemu dodać nową kategorię do systemu, udostępniając obraz i broszurę dla nowej kategorii. W poprzednim samouczku użyliśmy elementu GridView z elementem TemplateField i ImageField w celu wyświetlenia nazwy, opisu, obrazu i linku do pobrania broszury. Zreplikujmy tę funkcję dla tego samouczka, tworząc stronę zawierającą listę wszystkich istniejących kategorii i umożliwiającą utworzenie nowych.

Zacznij od otwarcia DisplayOrDownload.aspx strony z BinaryData folderu. Przejdź do widoku Źródło i skopiuj składnię deklaratywną GridView i ObjectDataSource, wklejając ją w <asp:Content> elemecie w elemecie .UploadInDetailsView.aspx Nie zapomnij również skopiować GenerateBrochureLink metody z klasy code-behind do DisplayOrDownload.aspxUploadInDetailsView.aspxklasy .

Kopiowanie i wklejanie składni deklaratywnej 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 pełnowymiarowy)

Po skopiowaniu składni deklaratywnej UploadInDetailsView.aspx i GenerateBrochureLink metody na 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 pełnowymiarowy)

Krok 4. KonfigurowanieCategoriesDataSourceelementu do obsługi wstawiania

Obiekt CategoriesDataSource ObjectDataSource używany przez Categories obiekt GridView obecnie nie zapewnia możliwości wstawiania danych. Aby umożliwić wstawianie za pośrednictwem tej kontroli źródła danych, musimy zamapować jej Insert metodę na metodę w jego obiekcie bazowym. CategoriesBLL W szczególności chcemy zamapować ją na metodę CategoriesBLL dodaną z powrotem w kroku 2. InsertWithPicture

Zacznij od kliknięcia linku Konfiguruj źródło danych z tagu inteligentnego ObjectDataSource. Pierwszy ekran przedstawia obiekt skonfigurowany do pracy ze źródłem danych. CategoriesBLL Pozostaw to ustawienie zgodnie z rzeczywistym ustawieniem i kliknij przycisk Dalej, aby przejść do ekranu Definiowanie metod danych. Przejdź do karty INSERT i wybierz metodę InsertWithPicture z listy rozwijanej. Kliknij przycisk Zakończ, aby zakończyć kreatora.

Konfigurowanie obiektu ObjectDataSource do używania metody InsertWithPicture

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

Uwaga

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 ukończeniu pracy kreatora źródło ObjectDataSource będzie teraz zawierać wartość jej InsertMethod właściwości, a także InsertParameters dla czterech kolumn kategorii, jak pokazano w poniższym deklaratywnej adiustacji:

<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

Zgodnie z opisem w temacie Omówienie 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, która obsługuje wstawianie. Dodajmy kontrolkę DetailsView do tej strony powyżej kontrolki GridView, która trwale renderuje interfejs wstawiania, umożliwiając użytkownikowi szybkie dodawanie nowej kategorii. Po dodaniu nowej kategorii w widoku DetailsView element GridView będzie automatycznie odświeżyć i wyświetlić nową kategorię.

Zacznij od przeciągnięcia kontrolki DetailsView z przybornika na Projektant powyżej kontrolki GridView, ustawiając jej ID właściwość na NewCategory i usuwając Height wartości właściwości iWidth. Z tagu inteligentnego DetailsView powiąż go z istniejącym 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ąż widok DetailsView z elementem CategoriesDataSource i Włącz wstawianie (kliknij, aby wyświetlić obraz o pełnym rozmiarze)

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

Należy pamiętać, że element DetailsView ma pięć elementów BoundFieldsCategoryID, , Description, NumberOfProductsi BrochurePath chociaż pole CategoryID BoundField nie jest renderowane w interfejsie wstawiania, CategoryNameponieważ jego InsertVisible właściwość jest ustawiona na false. Te pola granic istnieją, ponieważ są to kolumny zwracane przez metodę GetCategories() , która jest wywoływana przez obiekt ObjectDataSource w celu pobrania danych. W przypadku wstawiania nie chcemy jednak zezwalać użytkownikowi na określenie wartości .NumberOfProducts Ponadto musimy zezwolić im na przekazanie obrazu dla nowej kategorii, a także przekazanie pliku PDF dla broszury.

Usuń pole NumberOfProducts BoundField z widoku DetailsView, a następnie zaktualizuj HeaderText odpowiednio właściwości CategoryName pola i BrochurePath BoundFields do kategorii i broszury. Następnie przekonwertuj pole BrochurePath BoundField na pole szablonu i dodaj nowe pole szablonu dla obrazu, dając temu nowemu pola szablonu HeaderText wartość Obraz. Przenieś pole szablonu, Picture aby było między BrochurePath polami TemplateField i CommandField.

Zrzut ekranu przedstawiający okno pól z wyróżnioną pozycją TemplateField, Picture i HeaderText.

Rysunek 7. Powiązanie widoku DetailsView z elementem CategoriesDataSource i włączanie wstawiania

Jeśli pole BoundField przekonwertowano BrochurePath na pole szablonu za pomocą okna dialogowego Edytowanie pól, pole TemplateField zawiera element ItemTemplate, EditItemTemplatei InsertItemTemplate. Tylko te InsertItemTemplate elementy są potrzebne, więc możesz usunąć pozostałe dwa szablony. W tym momencie składnia deklaratywna elementu 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 obrazów

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

Z tagu inteligentnego DetailsView wybierz opcję Edytuj szablony, a następnie wybierz pozycję BrochurePath TemplateField s InsertItemTemplate 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 składnia deklaratywna pola szablonu będzie następująca:

<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 doda nową kategorię, chcemy mieć pewność, że broszura i obraz są poprawnego typu 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, musimy rozszerzyć Categories schemat, aby zawierał kolumnę, która przechwytuje typ pliku, aby ten typ mógł zostać wysłany do klienta za pośrednictwem Response.ContentType programu w DisplayCategoryPicture.aspxprogramie . Ponieważ nie mamy takiej kolumny, rozsądnie byłoby ograniczyć użytkowników tylko do podawania określonego typu pliku obrazu. Istniejące Categories obrazy tabeli to mapy bitowe, ale pliki JPG są bardziej odpowiednim formatem plików dla obrazów obsługiwanych w Internecie.

Jeśli użytkownik przekaże nieprawidłowy typ pliku, musimy anulować wstawianie i wyświetlić komunikat wskazujący problem. Dodaj kontrolkę Sieć Web Etykieta pod kontrolką DetailsView. Ustaw jej ID właściwość na UploadWarning, wyczyść jej Text właściwość, ustaw CssClass właściwość na Ostrzeżenie, a właściwości i VisibleEnableViewState na falsewartość . Klasa Warning CSS jest definiowana w pliku Styles.css i renderuje tekst w dużej, czerwonej, kursywie, pogrubionej czcionki.

Uwaga

W idealnym przypadku obiekty CategoryName i Description BoundFields zostaną przekonwertowane na pola szablonów i dostosowane do ich interfejsów wstawiania. Na Description przykład interfejs wstawiania prawdopodobnie lepiej nadaje się za pomocą wielowierszowego pola tekstowego. Ponieważ kolumna CategoryName nie akceptuje NULL wartości, należy dodać element RequiredFieldValidator, aby upewnić się, że użytkownik udostępnia wartość nowej nazwy kategorii. Te kroki są pozostawione jako ćwiczenie dla czytelnika. Zapoznaj się z tematem Dostosowywanie interfejsu modyfikacji danych , aby uzyskać szczegółowe informacje na temat rozszerzania interfejsów modyfikacji danych.

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

Gdy użytkownik wprowadzi wartości nowej kategorii i kliknie przycisk Wstaw, nastąpi powrót i zostanie rozwinięty wstawiony przepływ pracy. Najpierw zostanie wyzwolony zdarzenie DetailsViewItemInserting. Następnie wywoływana jest metoda ObjectDataSource Insert() , która powoduje dodanie nowego rekordu Categories do tabeli. Następnie zostanie wyzwolony zdarzenie DetailsViewItemInserted.

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 procedurę obsługi zdarzeń dla zdarzenia DetailsView i ItemInserting 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ływania się do BrochureUpload kontrolki FileUpload z szablonów elementu DetailsView. Następnie, jeśli broszura została przekazana, rozszerzenie przekazanego pliku zostanie zbadane. Jeśli rozszerzenie nie jest .PDF, zostanie wyświetlone ostrzeżenie, wstawianie zostanie anulowane, a wykonanie programu obsługi zdarzeń zakończy się.

Uwaga

Poleganie na przekazanym rozszerzeniu 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 przekazywanie jednego użytkownika nie zastępowało innego elementu. W tym samouczku spróbujemy użyć tej samej nazwy co przekazany plik. Jeśli istnieje już plik w ~/Brochures katalogu 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 , ale istnieje już plik o nazwie Meats.pdfMeats.pdf w ~/Brochures folderze, zmienimy zapisaną nazwę pliku na Meats-1.pdf. Jeśli tak istnieje, spróbujemy użyć Meats-2.pdfmetody i tak dalej, dopóki nie zostanie znaleziona unikatowa nazwa pliku.

Poniższy kod używa File.Exists(path) metody do określenia, czy plik już istnieje o określonej nazwie pliku. Jeśli tak, nadal spróbuje nowych nazw plików dla broszury, dopóki nie zostanie znaleziony konflikt.

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 s kontrolki SaveAs(path) FileUpload. Aby zaktualizować parametr objectDataSource 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 do bazy 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. Podobnie jak w kroku 6, użyjmy rozszerzenia pliku przekazanego obrazu, aby ustalić jego typ.

Categories Chociaż tabela zezwala NULL na wartości dla Picture kolumny, wszystkie kategorie mają obecnie obraz. Wymusimy, aby użytkownik podał obraz podczas dodawania nowej kategorii za pośrednictwem tej strony. Poniższy kod sprawdza, czy obraz został przekazany 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 powinien zostać umieszczony przed kodem z kroku 6, aby w przypadku wystąpienia problemu z przekazywaniem obrazu program obsługi zdarzeń zakończył się przed zapisaniem pliku broszury w systemie plików.

Przy założeniu, że został przekazany odpowiedni plik, przypisz przekazaną zawartość binarną do wartości parametru obrazu z następującym wierszem kodu:

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

KompletnaItemInsertingprocedura obsługi zdarzeń

Aby uzyskać kompletność, poniżej przedstawiono procedurę obsługi zdarzeń ItemInserting 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święć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.

Komunikat ostrzegawczy jest wyświetlany w przypadku przekazania nieprawidłowego typu pliku

Rysunek 9. Komunikat ostrzegawczy jest wyświetlany, jeśli przekazano nieprawidłowy typ pliku (kliknij, aby wyświetlić obraz pełnowymiarowy)

Po sprawdzeniu, czy strona wymaga przekazania obrazu i nie zaakceptuje plików innych niż PDF lub innych niż JPG, dodaj nową kategorię z prawidłowym obrazem JPG, pozostawiając pole Broszura puste. Po kliknięciu przycisku Wstaw strona zostanie wycofana, a nowy rekord zostanie dodany do Categories tabeli z przekazaną zawartością binarną obrazu przechowywaną bezpośrednio w bazie danych. Widok 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 pełnowymiarowy)

Powodem, dla którego nowy obraz nie jest wyświetlany, jest to, że DisplayCategoryPicture.aspx strona zwracająca obraz określonej kategorii jest skonfigurowany do przetwarzania map bitowych, które mają nagłówek OLE. Ten nagłówek 78 bajtów jest usuwany z zawartości binarnej Picture kolumny, zanim zostaną one wysłane z powrotem 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ż istnieją teraz mapy bitowe z nagłówkami OLE i grupami JPG w Categories tabeli, musimy zaktualizować DisplayCategoryPicture.aspx tak, aby nagłówek OLE usuwał oryginalne osiem kategorii i pomija ten usuwanie dla nowszych rekordów kategorii. W następnym samouczku sprawdzimy, jak zaktualizować istniejący obraz rekordu i zaktualizujemy wszystkie stare obrazy kategorii, tak aby były to pliki JPG. Na razie jednak użyj następującego kodu, DisplayCategoryPicture.aspx 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 jest teraz poprawnie renderowany w elemecie 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 obliczu 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. Dlatego 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ż być w grę 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ąpi powrót, a zdarzenie DetailsView zostanie ItemInserting wyzwolony, zapisując broszurę w systemie plików serwera internetowego. Następnie wywoływana jest metoda ObjectDataSourceInsert(), która wywołuje metodę s InsertWithPicture klasy, która wywołuje CategoriesBLL metodę CategoriesTableAdapter sInsertWithPicture.

Teraz co się stanie, jeśli baza danych jest w trybie offline lub jeśli w instrukcji INSERT SQL występuje błąd? 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 siedzący w systemie plików serwera internetowego! Ten plik musi zostać usunięty w obliczu wyjątku podczas wstawiania przepływu pracy.

Jak wspomniano wcześniej w samouczku Obsługa wyjątków BLL i DAL-Level w samouczku dotyczącym strony ASP.NET , gdy wyjątek jest zgłaszany z poziomu głębi architektury, jest bąbelkowany przez różne warstwy. W warstwie prezentacji możemy określić, czy wystąpił wyjątek z 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ć procedurę obsługi zdarzeń, ItemInserted która sprawdza, czy wystąpił wyjątek, a jeśli tak, usuwa plik określony przez parametr ObjectDataSource 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 kilka kroków, które należy wykonać w celu zapewnienia internetowego interfejsu do dodawania rekordów zawierających dane binarne. Jeśli dane binarne są przechowywane bezpośrednio w bazie danych, prawdopodobieństwo, że 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. Przekazane dane można następnie zapisać w systemie plików serwera internetowego lub przypisać do parametru źródła danych w procedurze obsługi zdarzeń elementu DetailsView ItemInserting .

Zapisywanie danych binarnych w systemie plików wymaga więcej planowania niż zapisywanie danych bezpośrednio w bazie danych. Aby uniknąć zastąpienia innego użytkownika, należy wybrać schemat nazewnictwa. Ponadto należy wykonać dodatkowe kroki w celu usunięcia przekazanego pliku, jeśli wstawianie bazy danych zakończy się niepowodzeniem.

Teraz mamy możliwość dodawania nowych kategorii do systemu z broszurą i obrazem, ale jeszcze nie przyjrzeliśmy się, jak zaktualizować istniejące dane binarne kategorii lub jak poprawnie usunąć dane binarne dla usuniętej kategorii. Zapoznamy się z tymi dwoma tematami w następnym samouczku.

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 do niego dotrzeć pod adresem mitchell@4GuysFromRolla.com. Lub za pośrednictwem swojego bloga, który można znaleźć na stronie http://ScottOnWriting.NET.

Specjalne podziękowania

Ta seria samouczków została sprawdzona przez wielu pomocnych recenzentów. Recenzenci na potrzeby tego samouczka to Dave Gardner, Teresa Murphy i Bernadette Leigh. Chcesz przejrzeć nadchodzące artykuły MSDN? Jeśli tak, upuść mi wiersz pod adresem mitchell@4GuysFromRolla.com.