새 레코드를 추가할 때 파일 업로드 옵션 포함(VB)

작성자 : Scott Mitchell

PDF 다운로드

이 자습서에서는 사용자가 텍스트 데이터를 입력하고 이진 파일을 업로드할 수 있는 웹 인터페이스를 만드는 방법을 보여 줍니다. 이진 데이터를 저장하는 데 사용할 수 있는 옵션을 설명하기 위해 한 파일은 데이터베이스에 저장되고 다른 파일은 파일 시스템에 저장됩니다.

소개

이전 두 자습서에서는 애플리케이션의 데이터 모델과 연결된 이진 데이터를 저장하는 기술을 살펴보고, FileUpload 컨트롤을 사용하여 클라이언트에서 웹 서버로 파일을 보내는 방법을 살펴보았습니다. 이 이진 데이터를 데이터 웹 컨트롤에 표시하는 방법을 살펴보았습니다. 하지만 업로드된 데이터를 데이터 모델과 연결하는 방법에 대해서는 아직 설명하지 않았습니다.

이 자습서에서는 새 범주를 추가하는 웹 페이지를 만듭니다. 범주 이름 및 설명에 대한 TextBox 외에도 이 페이지에는 두 개의 FileUpload 컨트롤이 새 범주 그림과 브로슈어에 대해 하나씩 포함되어야 합니다. 업로드된 그림은 새 레코드의 Picture 열에 직접 저장되는 반면 브로셔는 새 레코드 BrochurePath 열에 저장된 파일 경로와 함께 폴더에 저장 ~/Brochures 됩니다.

이 새 웹 페이지를 만들기 전에 아키텍처를 업데이트해야 합니다. 기본 쿼리는 CategoriesTableAdapter 열을 검색 Picture 하지 않습니다. 따라서 자동 생성된 Insert 메서드에는 , DescriptionBrochurePath 필드에 대한 CategoryName입력만 있습니다. 따라서 TableAdapter에서 네 개의 Categories 필드를 모두 묻는 추가 메서드를 만들어야 합니다. CategoriesBLL 비즈니스 논리 계층의 클래스도 업데이트해야 합니다.

1단계: 에 메서드 추가InsertWithPictureCategoriesTableAdapter

데이터 액세스 계층 만들기 자습서에서 다시 만들 CategoriesTableAdapter 때 기본 쿼리를 기반으로 , UPDATEDELETE 문을 자동으로 생성INSERT하도록 구성했습니다. 또한 , 및 Delete메서드InsertUpdate를 만든 DB 직접 접근 방식을 사용하도록 TableAdapter에 지시했습니다. 이러한 메서드는 자동으로 생성된 INSERT, UPDATEDELETE 문을 실행하므로 기본 쿼리에서 반환된 열에 따라 입력 매개 변수를 허용합니다. 파일 업로드 자습서에서는 열을 사용하도록 기본 쿼리를 보강 CategoriesTableAdapter 했습니다BrochurePath.

CategoriesTableAdapter 기본 쿼리는 열을 참조 Picture 하지 않으므로 새 레코드를 추가하거나 기존 레코드를 열 값 Picture 으로 업데이트할 수 없습니다. 이 정보를 캡처하기 위해 TableAdapter에서 이진 데이터가 포함된 레코드를 삽입하는 데 특별히 사용되는 새 메서드를 만들거나 자동 생성된 INSERT 문을 사용자 지정할 수 있습니다. 자동 생성 INSERT 문을 사용자 지정하는 문제는 마법사에서 사용자 지정을 덮어쓸 위험이 있다는 것입니다. 예를 들어 열 사용을 포함하도록 문을 사용자 지정했다고 INSERT 상상해 보십시오 Picture . 그러면 범주의 Insert 그림 이진 데이터에 대한 추가 입력 매개 변수를 포함하도록 TableAdapter 메서드가 업데이트됩니다. 그런 다음 이 DAL 메서드를 사용하고 프레젠테이션 계층을 통해 이 BLL 메서드를 호출하는 비즈니스 논리 계층에 메서드를 만들 수 있으며 모든 것이 멋지게 작동합니다. 즉, 다음에 TableAdapter 구성 마법사를 통해 TableAdapter를 구성할 때까지입니다. 마법사가 완료되면 문에 INSERT 대한 사용자 지정을 덮어쓰고 메서드가 Insert 이전 형식으로 되돌리기 코드가 더 이상 컴파일되지 않습니다.

참고

이 성가신 것은 임시 SQL 문 대신 저장 프로시저를 사용할 때 문제가 되지 않습니다. 이후 자습서에서는 데이터 액세스 계층의 임시 SQL 문 대신 저장 프로시저를 사용하여 살펴봅니다.

이러한 잠재적인 문제를 방지하기 위해 자동 생성된 SQL 문을 사용자 지정하는 대신 TableAdapter에 대한 새 메서드를 만들 수 있습니다. 라는 InsertWithPicture이 메서드는 , , DescriptionBrochurePath및 열에 CategoryName대한 값을 수락하고 Picture 4개의 값을 모두 새 레코드에 저장하는 문을 실행 INSERT 합니다.

형식화된 데이터 세트를 열고 Designer 머리글을 마우스 오른쪽 단추로 클릭하고 CategoriesTableAdapter 상황에 맞는 메뉴에서 쿼리 추가를 선택합니다. 그러면 TableAdapter 쿼리 구성 마법사가 시작됩니다. 이 마법사는 TableAdapter 쿼리가 데이터베이스에 액세스하는 방법을 묻는 것으로 시작됩니다. SQL 문 사용을 선택하고 다음을 클릭합니다. 다음 단계에서는 생성할 쿼리 형식을 묻는 메시지를 표시합니다. 테이블에 새 레코드를 추가하는 쿼리를 만들고 있으므로 INSERT를 Categories 선택하고 다음을 클릭합니다.

INSERT 옵션 선택

그림 1: INSERT 옵션 선택(전체 크기 이미지를 보려면 클릭)

이제 SQL 문을 지정 INSERT 해야 합니다. 마법사는 TableAdapter의 기본 쿼리에 해당하는 문을 자동으로 제안 INSERT 합니다. 이 경우 , INSERTDescriptionBrochurePath 값을 삽입하는 CategoryName문입니다. 다음과 같이 열이 Picture 매개 변수와 함께 @Picture 포함되도록 문을 업데이트합니다.

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

마법사의 마지막 화면에서 새 TableAdapter 메서드의 이름을 지정하도록 요청합니다. 마침을 입력 InsertWithPicture 하고 클릭합니다.

새 TableAdapter 메서드 InsertWithPicture의 이름을 지정합니다.

그림 2: 새 TableAdapter 메서드 InsertWithPicture 의 이름을 지정합니다(전체 크기 이미지를 보려면 클릭).

2단계: 비즈니스 논리 계층 업데이트

프레젠테이션 계층은 데이터 액세스 계층으로 직접 이동하도록 바이패스하는 대신 비즈니스 논리 계층과만 인터페이스해야 하므로 방금 만든 DAL 메서드(InsertWithPicture)를 호출하는 BLL 메서드를 만들어야 합니다. 이 자습서에서는 라는 클래스 InsertWithPicture 에서 CategoriesBLL 입력 3 String 개 및 배열로 허용하는 메서드를 Byte 만듭니다. 입력 매개 변수는 String 범주의 이름, 설명 및 브로셔 파일 경로에 대한 반면 Byte 배열은 범주 그림의 이진 콘텐츠에 대한 것입니다. 다음 코드와 같이 이 BLL 메서드는 해당 DAL 메서드를 호출합니다.

<System.ComponentModel.DataObjectMethodAttribute _
    (System.ComponentModel.DataObjectMethodType.Insert, False)> _
Public Sub InsertWithPicture(categoryName As String, description As String, _
    brochurePath As String, picture() As Byte)
    
    Adapter.InsertWithPicture(categoryName, description, brochurePath, picture)
End Sub

참고

BLL에 메서드를 추가하기 전에 Typed DataSet을 InsertWithPicture 저장했는지 확인합니다. CategoriesTableAdapter 클래스 코드는 Typed DataSet을 기반으로 자동 생성되므로 Typed DataSet Adapter 에 변경 내용을 먼저 저장하지 않으면 속성이 메서드에 대해 InsertWithPicture 알 수 없습니다.

3단계: 기존 범주 및 해당 이진 데이터 나열

이 자습서에서는 최종 사용자가 시스템에 새 범주를 추가하여 새 범주에 대한 그림과 브로셔를 제공할 수 있는 페이지를 만듭니다. 이전 자습서에서는 TemplateField 및 ImageField가 있는 GridView를 사용하여 각 범주의 이름, 설명, 그림 및 링크를 표시하여 브로셔를 다운로드했습니다. 이 자습서의 기능을 복제하여 기존 범주를 모두 나열하고 새 범주를 만들 수 있는 페이지를 만들어 보겠습니다.

먼저 폴더에서 BinaryData 페이지를 엽니다DisplayOrDownload.aspx. 원본 보기로 이동하여 GridView 및 ObjectDataSource의 선언적 구문을 복사하여 의 요소 UploadInDetailsView.aspx내에 <asp:Content> 붙여넣습니다. 또한 의 코드 숨김 클래스 DisplayOrDownload.aspx 에서 로 메서드를 복사 GenerateBrochureLink 하는 것을 UploadInDetailsView.aspx잊지 마세요.

선언적 구문을 복사하여 DisplayOrDownload.aspx 붙여넣습니다UploadInDetailsView.aspx

그림 3: 선언적 구문을 복사하여 에 DisplayOrDownload.aspxUploadInDetailsView.aspx 붙여넣습니다(전체 크기 이미지를 보려면 클릭).

선언적 구문 및 GenerateBrochureLink 메서드 UploadInDetailsView.aspx 를 페이지에 복사한 후 브라우저를 통해 페이지를 확인하여 모든 항목이 올바르게 복사되었는지 확인합니다. 브로슈어와 범주 사진을 다운로드하는 링크가 포함된 8개의 범주를 나열하는 GridView가 표시됩니다.

이제 해당 이진 데이터와 함께 각 범주가 표시됩니다.

그림 4: 이제 이진 데이터와 함께 각 범주가 표시됩니다(전체 크기 이미지를 보려면 클릭).

4단계: 삽입을CategoriesDataSource지원하도록 구성

GridView에서 Categories 사용하는 ObjectDataSource는 CategoriesDataSource 현재 데이터를 삽입하는 기능을 제공하지 않습니다. 이 데이터 소스 제어를 통해 삽입을 지원하려면 해당 메서드를 기본 개체 CategoriesBLL의 메서드에 매핑 Insert 해야 합니다. 특히 2InsertWithPicture단계에서 다시 추가한 CategoriesBLL 메서드에 매핑하려고 합니다.

먼저 ObjectDataSource의 스마트 태그에서 데이터 원본 구성 링크를 클릭합니다. 첫 번째 화면에는 데이터 원본이 작업하도록 구성된 개체인 CategoriesBLL가 표시됩니다. 이 설정을 그대로 두고 다음을 클릭하여 데이터 메서드 정의 화면으로 이동합니다. INSERT 탭으로 이동하고 드롭다운 목록에서 메서드를 선택합니다 InsertWithPicture . 마침을 클릭하여 마법사를 완료합니다.

InsertWithPicture 메서드를 사용하도록 ObjectDataSource 구성

그림 5: 메서드를 사용하도록 InsertWithPicture ObjectDataSource 구성(전체 크기 이미지를 보려면 클릭)

참고

마법사를 완료하면 Visual Studio에서 데이터 웹 컨트롤 필드를 다시 생성할 필드 및 키를 새로 고칠지 물어볼 수 있습니다. 예를 선택하면 만들 수 있는 모든 필드 사용자 지정을 덮어쓰기 때문에 아니요를 선택합니다.

마법사를 완료한 후 ObjectDataSource는 이제 다음 선언적 태그와 같이 해당 InsertMethod 속성의 값과 InsertParameters 4개의 범주 열에 대한 값을 포함합니다.

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

5단계: 삽입 인터페이스 만들기

먼저 데이터 삽입, 업데이트 및 삭제 개요에서 설명한 것처럼 DetailsView 컨트롤은 삽입을 지원하는 데이터 소스 컨트롤로 작업할 때 활용할 수 있는 기본 제공 삽입 인터페이스를 제공합니다. 사용자가 새 범주를 빠르게 추가할 수 있도록 삽입 인터페이스를 영구적으로 렌더링하는 GridView 위의 이 페이지에 DetailsView 컨트롤을 추가해 보겠습니다. DetailsView에 새 범주를 추가하면 그 아래에 있는 GridView가 자동으로 새로 고쳐지고 새 범주가 표시됩니다.

먼저 Toolbox에서 GridView 위의 Designer DetailsView를 끌어 속성을 ID 로 설정하고 및 Width 속성 값을 지웁 HeightNewCategory 니까? DetailsView의 스마트 태그에서 기존 CategoriesDataSource 태그에 바인딩한 다음 삽입 사용 확인란을 검사.

CategoryID 속성이 NewCategory로 설정되고 Height 및 Width 속성 값이 비어 있고 삽입 사용 확인란이 선택된 DetailsView의 스크린샷

그림 6: DetailsView를 CategoriesDataSource 에 바인딩하고 삽입 사용(전체 크기 이미지를 보려면 클릭)

삽입 인터페이스에서 DetailsView를 영구적으로 렌더링하려면 속성을 DefaultModeInsert로 설정합니다.

DetailsView에는 5개의 BoundField , , CategoryName, 및 가 있지만 BrochurePathCategoryID 해당 속성이 로 설정되어 False있으므로 InsertVisible BoundField가 삽입 인터페이스에서 렌더링되지 않습니다. NumberOfProductsDescriptionCategoryID 이러한 BoundFields는 ObjectDataSource가 해당 데이터를 검색하기 위해 호출하는 메서드에서 반환 GetCategories() 되는 열이기 때문에 존재합니다. 그러나 삽입의 경우 사용자가 에 대한 NumberOfProducts값을 지정하도록 하고 싶지 않습니다. 또한 새 범주의 사진을 업로드하고 브로셔에 대한 PDF를 업로드하도록 허용해야 합니다.

NumberOfProducts DetailsView에서 BoundField를 모두 제거한 다음, 및 BrochurePath BoundFields의 CategoryName 속성을 각각 범주 및 브로슈어로 업데이트 HeaderText 합니다. 다음으로, BoundField를 BrochurePath TemplateField로 변환하고 그림에 대한 새 TemplateField를 추가하여 이 새 TemplateField에 HeaderText 그림 값을 제공합니다. Picture TemplateField와 CommandField 사이에 있도록 TemplateField를 BrochurePath 이동합니다.

TemplateField, Picture 및 HeaderText가 강조 표시된 필드 창을 보여 주는 스크린샷

그림 7: DetailsView를 CategoriesDataSource 에 바인딩하고 삽입 사용

필드 편집 대화 상자를 통해 BoundField를 TemplateField로 변환 BrochurePath 한 경우 TemplateField에는 , EditItemTemplateInsertItemTemplate가 포함됩니다ItemTemplate. 그러나 만 InsertItemTemplate 필요하므로 다른 두 템플릿을 자유롭게 제거할 수 있습니다. 이 시점에서 DetailsView의 선언적 구문은 다음과 같이 표시됩니다.

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

브로셔 및 그림 필드에 대한 FileUpload 컨트롤 추가

현재 BrochurePath TemplateField에는 InsertItemTemplate TextBox가 포함되어 있지만 Picture TemplateField에는 템플릿이 포함되어 있지 않습니다. FileUpload 컨트롤을 사용하려면 이 두 TemplateField를 InsertItemTemplate 업데이트해야 합니다.

DetailsView의 스마트 태그에서 템플릿 편집 옵션을 선택한 다음 드롭다운 목록에서 TemplateField를 InsertItemTemplate 선택합니다BrochurePath. TextBox를 제거한 다음 FileUpload 컨트롤을 도구 상자에서 템플릿으로 끌어옵니다. FileUpload 컨트롤 s를 IDBrochureUpload설정합니다. 마찬가지로 FileUpload 컨트롤을 TemplateField의 InsertItemTemplatePicture 추가합니다. 이 FileUpload 컨트롤 s를 IDPictureUpload설정합니다.

InsertItemTemplate에 FileUpload 컨트롤 추가

그림 8: FileUpload 컨트롤을 에 InsertItemTemplate 추가합니다(전체 크기 이미지를 보려면 클릭).

이러한 추가를 수행한 후 두 TemplateField의 선언적 구문은 다음과 같습니다.

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

사용자가 새 범주를 추가할 때 브로셔와 그림이 올바른 파일 형식인지 확인하려고 합니다. 브로셔의 경우 사용자가 PDF를 제공해야 합니다. 그림의 경우 사용자가 이미지 파일을 업로드해야 하지만 이미지 파일 또는 GIF 또는 JPG와 같은 특정 형식의 이미지 파일 만 허용합니까 ? 다른 파일 형식을 허용하려면 에서 이 형식을 클라이언트 Response.ContentTypeDisplayCategoryPicture.aspx로 보낼 수 있도록 파일 형식을 캡처하는 열을 포함하도록 스키마를 확장 Categories 해야 합니다. 이러한 열이 없으므로 사용자가 특정 이미지 파일 형식만 제공하도록 제한하는 것이 중요합니다. Categories 테이블의 기존 이미지는 비트맵이지만 JPG는 웹을 통해 제공되는 이미지에 더 적합한 파일 형식입니다.

사용자가 잘못된 파일 형식을 업로드하는 경우 삽입을 취소하고 문제를 나타내는 메시지를 표시해야 합니다. DetailsView 아래에 레이블 웹 컨트롤을 추가합니다. 속성을 IDUploadWarning설정하고, 속성을 Text 지우고Visible, 속성을 Warning으로 설정하고, 및 EnableViewState 속성을 로 False설정합니다CssClass. Warning CSS 클래스는 에서 Styles.css 정의되며 텍스트를 크고 빨간색이며 기울임꼴로 굵은 글꼴로 렌더링합니다.

참고

이상적으로 및 CategoryNameDescription BoundFields는 TemplateFields 및 사용자 지정된 삽입 인터페이스로 변환됩니다. Description 예를 들어 삽입 인터페이스는 여러 줄 텍스트 상자를 통해 더 적합할 수 있습니다. 열이 CategoryName 값을 허용하지 NULL 않으므로 사용자가 새 범주 이름에 대한 값을 제공하려면 RequiredFieldValidator를 추가해야 합니다. 이러한 단계는 독자에게 연습으로 남아 있습니다. 데이터 수정 인터페이스를 보강하는 방법에 대한 자세한 내용은 데이터 수정 인터페이스 사용자 지정 을 참조하세요.

6단계: 업로드된 브로슈어를 웹 서버의 파일 시스템에 저장

사용자가 새 범주의 값을 입력하고 삽입 단추를 클릭하면 포스트백이 발생하고 삽입 워크플로가 펼쳐진다. 먼저 DetailsView의 ItemInserting 이벤트가 발생합니다. 다음으로 ObjectDataSource의 Insert() 메서드가 호출되어 테이블에 새 레코드가 Categories 추가됩니다. 그 후 DetailsView의 ItemInserted 이벤트가 발생합니다.

ObjectDataSource의 Insert() 메서드를 호출하기 전에 먼저 사용자가 적절한 파일 형식을 업로드했는지 확인한 다음 브로셔 PDF를 웹 서버의 파일 시스템에 저장해야 합니다. DetailsView 이벤트에 ItemInserting 대한 이벤트 처리기를 만들고 다음 코드를 추가합니다.

' Reference the FileUpload controls
Dim BrochureUpload As FileUpload = _
    CType(NewCategory.FindControl("BrochureUpload"), FileUpload)
If BrochureUpload.HasFile Then
    ' Make sure that a PDF has been uploaded
    If String.Compare(System.IO.Path.GetExtension _
        (BrochureUpload.FileName), ".pdf", True) <> 0 Then
        UploadWarning.Text = _
            "Only PDF documents may be used for a category's brochure."
        UploadWarning.Visible = True
        e.Cancel = True
        Exit Sub
    End If
End If

이벤트 처리기는 DetailsView 템플릿에서 BrochureUpload FileUpload 컨트롤을 참조하여 시작합니다. 그런 다음 브로셔가 업로드된 경우 업로드된 파일의 확장자를 검사합니다. 확장이 .PDF 않으면 경고가 표시되고 삽입이 취소되고 이벤트 처리기의 실행이 종료됩니다.

참고

업로드된 파일의 확장명 사용은 업로드된 파일이 PDF 문서인지 확인하는 확실한 기술이 아닙니다. 사용자에게 확장 .Brochure명이 있는 유효한 PDF 문서가 있거나 PDF가 아닌 문서를 가져와서 확장명이 .pdf 지정되었을 수 있습니다. 파일 형식을 보다 결정적으로 확인하려면 파일의 이진 콘텐츠를 프로그래밍 방식으로 검사해야 합니다. 그러나 이러한 철저한 접근 방식은 종종 과잉입니다. 대부분의 시나리오에서 확장을 확인하는 것으로 충분합니다.

파일 업로드 자습서에서 설명한 대로 파일을 파일 시스템에 저장할 때 한 사용자의 업로드가 다른 사용자를 덮어쓰지 않도록 주의해야 합니다. 이 자습서에서는 업로드된 파일과 동일한 이름을 사용하려고 합니다. 그러나 디렉터리에 동일한 파일 이름이 있는 파일이 ~/Brochures 이미 있는 경우 고유한 이름을 찾을 때까지 끝에 숫자를 추가합니다. 예를 들어 사용자가 라는 Meats.pdf브로슈어 파일을 업로드하지만 폴더에 ~/Brochures 라는 Meats.pdf 파일이 이미 있는 경우 저장된 파일 이름을 로 Meats-1.pdf변경합니다. 이 경우 고유한 파일 이름을 찾을 때까지 , 등을 시도 Meats-2.pdf합니다.

다음 코드는 메서드를 File.Exists(path) 사용하여 지정된 파일 이름을 가진 파일이 이미 있는지 확인합니다. 그렇다면 충돌을 찾을 때까지 브로셔에 대한 새 파일 이름을 계속 시도합니다.

Const BrochureDirectory As String = "~/Brochures/"
Dim brochurePath As String = BrochureDirectory & BrochureUpload.FileName
Dim fileNameWithoutExtension As String = _
    System.IO.Path.GetFileNameWithoutExtension(BrochureUpload.FileName)
Dim iteration As Integer = 1
While System.IO.File.Exists(Server.MapPath(brochurePath))
    brochurePath = String.Concat(BrochureDirectory, _
        fileNameWithoutExtension, "-", iteration, ".pdf")
    iteration += 1
End While

유효한 파일 이름이 발견되면 파일을 파일 시스템에 저장해야 하며 이 파일 이름이 데이터베이스에 기록되도록 ObjectDataSource 값을 brochurePath``InsertParameter 업데이트해야 합니다. 파일 업로드 자습서에서 살본 것처럼 FileUpload 컨트롤의 SaveAs(path) 메서드를 사용하여 파일을 저장할 수 있습니다. ObjectDataSource의 매개 변수를 brochurePath 업데이트하려면 컬렉션을 사용합니다 e.Values .

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

7단계: 업로드된 그림을 데이터베이스에 저장

업로드된 그림을 새 Categories 레코드에 저장하려면 DetailsView ItemInserting 이벤트의 ObjectDataSource picture 매개 변수에 업로드된 이진 콘텐츠를 할당해야 합니다. 그러나 이 할당을 수행하기 전에 먼저 업로드된 그림이 다른 이미지 형식이 아닌 JPG인지 확인해야 합니다. 6단계에서와 같이 업로드된 그림의 파일 확장자를 사용하여 해당 형식을 확인해 보겠습니다.

테이블은 Categories 열에 Picture 대한 값을 허용 NULL 하지만 모든 범주에는 현재 그림이 있습니다. 이 페이지를 통해 새 범주를 추가할 때 사용자가 그림을 제공하도록 강제해 보겠습니다. 다음 코드는 그림이 업로드되었고 적절한 확장이 있는지 확인합니다.

' Reference the FileUpload controls
Dim PictureUpload As FileUpload = _
    CType(NewCategory.FindControl("PictureUpload"), FileUpload)
If PictureUpload.HasFile Then
    ' Make sure that a JPG has been uploaded
    If  String.Compare(System.IO.Path.GetExtension(PictureUpload.FileName), _
            ".jpg", True) <> 0 AndAlso _
        String.Compare(System.IO.Path.GetExtension(PictureUpload.FileName), _
            ".jpeg", True) <> 0 Then
        
        UploadWarning.Text = _
            "Only JPG documents may be used for a category's picture."
        UploadWarning.Visible = True
        e.Cancel = True
        Exit Sub
    End If
Else
    ' No picture uploaded!
    UploadWarning.Text = _
        "You must provide a picture for the new category."
    UploadWarning.Visible = True
    e.Cancel = True
    Exit Sub
End If

이 코드는 그림 업로드에 문제가 있는 경우 브로셔 파일이 파일 시스템에 저장되기 전에 이벤트 처리기가 종료되도록 6단계의 코드 앞에 배치해야 합니다.

적절한 파일이 업로드되었다고 가정하면 다음 코드 줄을 사용하여 업로드된 이진 콘텐츠를 그림 매개 변수 값에 할당합니다.

' Set the value of the picture parameter
e.Values("picture") = PictureUpload.FileBytes

전체ItemInserting이벤트 처리기

완전성을 위해 전체 이벤트 처리기는 다음과 같습니다 ItemInserting .

Protected Sub NewCategory_ItemInserting _
    (sender As Object, e As DetailsViewInsertEventArgs) _
    Handles NewCategory.ItemInserting
    
    ' Reference the FileUpload controls
    Dim PictureUpload As FileUpload = _
        CType(NewCategory.FindControl("PictureUpload"), FileUpload)
    If PictureUpload.HasFile Then
        ' Make sure that a JPG has been uploaded
        If  String.Compare(System.IO.Path.GetExtension(PictureUpload.FileName), _
                ".jpg", True) <> 0 AndAlso _
            String.Compare(System.IO.Path.GetExtension(PictureUpload.FileName), _
                ".jpeg", True) <> 0 Then
            
            UploadWarning.Text = _
                "Only JPG documents may be used for a category's picture."
            UploadWarning.Visible = True
            e.Cancel = True
            Exit Sub
        End If
    Else
        ' No picture uploaded!
        UploadWarning.Text = _
            "You must provide a picture for the new category."
        UploadWarning.Visible = True
        e.Cancel = True
        Exit Sub
    End If
    ' Set the value of the picture parameter
    e.Values("picture") = PictureUpload.FileBytes
    ' Reference the FileUpload controls
    Dim BrochureUpload As FileUpload = _
        CType(NewCategory.FindControl("BrochureUpload"), FileUpload)
    If BrochureUpload.HasFile Then
        ' Make sure that a PDF has been uploaded
        If String.Compare(System.IO.Path.GetExtension(BrochureUpload.FileName), _
            ".pdf", True) <> 0 Then
            
            UploadWarning.Text = _
                "Only PDF documents may be used for a category's brochure."
            UploadWarning.Visible = True
            e.Cancel = True
            Exit Sub
        End If
        Const BrochureDirectory As String = "~/Brochures/"
        Dim brochurePath As String = BrochureDirectory & BrochureUpload.FileName
        Dim fileNameWithoutExtension As String = _
            System.IO.Path.GetFileNameWithoutExtension(BrochureUpload.FileName)
        Dim iteration As Integer = 1
        While System.IO.File.Exists(Server.MapPath(brochurePath))
            brochurePath = String.Concat(BrochureDirectory, _
                fileNameWithoutExtension, "-", iteration, ".pdf")
            iteration += 1
        End While
        ' Save the file to disk and set the value of the brochurePath parameter
        BrochureUpload.SaveAs(Server.MapPath(brochurePath))
        e.Values("brochurePath") = brochurePath
    End If
End Sub

8단계: 페이지 수정DisplayCategoryPicture.aspx

잠시 시간을 내어 지난 몇 단계에서 만든 삽입 인터페이스 및 ItemInserting 이벤트 처리기를 테스트해 보겠습니다. 브라우저를 UploadInDetailsView.aspx 통해 페이지를 방문하여 범주를 추가하려고 하지만 그림을 생략하거나 JPG가 아닌 그림이나 PDF가 아닌 브로셔를 지정합니다. 이러한 경우 오류 메시지가 표시되고 삽입 워크플로가 취소됩니다.

잘못된 파일 형식이 업로드되면 경고 메시지가 표시됩니다.

그림 9: 잘못된 파일 형식이 업로드되면 경고 메시지가 표시됩니다(전체 크기 이미지를 보려면 클릭).

페이지에 그림을 업로드해야 하고 PDF가 아닌 파일이나 JPG가 아닌 파일을 허용하지 않는지 확인한 후에는 유효한 JPG 그림이 있는 새 범주를 추가하여 브로슈어 필드를 비워 둡니다. 삽입 단추를 클릭하면 페이지가 포스트백되고 업로드된 이미지의 이진 콘텐츠가 데이터베이스에 Categories 직접 저장된 새 레코드가 테이블에 추가됩니다. GridView가 업데이트되고 새로 추가된 범주에 대한 행이 표시되지만 그림 10에서 알 수 있듯이 새 범주의 그림이 올바르게 렌더링되지 않습니다.

새 범주의 그림이 표시되지 않음

그림 10: 새 범주의 그림이 표시되지 않음(전체 크기 이미지를 보려면 클릭)

새 그림이 표시되지 DisplayCategoryPicture.aspx 않는 이유는 지정된 범주의 그림을 반환하는 페이지가 OLE 헤더가 있는 비트맵을 처리하도록 구성되었기 때문입니다. 이 78바이트 헤더는 클라이언트로 다시 전송되기 전에 열의 이진 콘텐츠에서 Picture 제거됩니다. 그러나 새 범주에 대해 방금 업로드한 JPG 파일에는 이 OLE 헤더가 없습니다. 따라서 이미지의 이진 데이터에서 유효한 필요한 바이트가 제거됩니다.

이제 테이블에 OLE 헤더와 JPG Categories 가 있는 비트맵이 모두 있으므로 원래 8개 범주에 대해 OLE 헤더 제거를 수행하고 최신 범주 레코드에 대해 이 제거를 무시하도록 업데이트 DisplayCategoryPicture.aspx 해야 합니다. 다음 자습서에서는 기존 레코드의 이미지를 업데이트하는 방법을 살펴보고 JPG가 되도록 모든 이전 범주 그림을 업데이트합니다. 하지만 지금은 의 다음 코드를 DisplayCategoryPicture.aspx 사용하여 원래 8개 범주에 대해서만 OLE 헤더를 제거합니다.

Protected Sub Page_Load(sender As Object, e As EventArgs) Handles Me.Load
    Dim categoryID As Integer = Convert.ToInt32(Request.QueryString("CategoryID"))
    ' Get information about the specified category
    Dim categoryAPI As New CategoriesBLL()
    Dim categories As Northwind.CategoriesDataTable = _
        categoryAPI.GetCategoryWithBinaryDataByCategoryID(categoryID)
    Dim category As Northwind.CategoriesRow = categories(0)
    If categoryID <= 8 Then
        ' 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 OleHeaderLength As Integer = 78
        Dim strippedImageLength As Integer = _
            category.Picture.Length - OleHeaderLength
        Dim strippedImageData(strippedImageLength) As Byte
        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)
    End If
End Sub

이 변경으로 JPG 이미지는 이제 GridView에서 올바르게 렌더링됩니다.

새 범주에 대한 JPG 이미지가 올바르게 렌더링됨

그림 11: 새 범주에 대한 JPG 이미지가 올바르게 렌더링됨(전체 크기 이미지를 보려면 클릭)

9단계: 예외의 얼굴에서 브로슈어 삭제

웹 서버의 파일 시스템에 이진 데이터를 저장하는 문제 중 하나는 데이터 모델과 해당 이진 데이터 간의 연결이 끊어진다는 것입니다. 따라서 레코드가 삭제될 때마다 파일 시스템의 해당 이진 데이터도 제거해야 합니다. 삽입할 때도 이 동작이 발생할 수 있습니다. 사용자가 유효한 그림과 브로슈어를 지정하여 새 범주를 추가하는 시나리오를 고려합니다. 삽입 단추를 클릭하면 포스트백이 발생하고 DetailsView의 ItemInserting 이벤트가 발생하여 브로셔가 웹 서버의 파일 시스템에 저장됩니다. 다음으로 ObjectDataSource의 Insert() 메서드가 호출됩니다. 이 메서드는 s InsertWithPicture 메서드를 CategoriesBLL 호출하는 클래스의 InsertWithPicture 메서드를 CategoriesTableAdapter 호출합니다.

이제 데이터베이스가 오프라인 상태이거나 SQL 문에 INSERT 오류가 있는 경우 어떻게 됩니까? INSERT가 실패하므로 데이터베이스에 새 범주 행이 추가되지 않습니다. 그러나 업로드된 브로셔 파일이 여전히 웹 서버의 파일 시스템에 있습니다! 이 파일은 삽입 워크플로 중에 예외가 발생할 경우 삭제해야 합니다.

ASP.NET 페이지의 BLL 처리 및 DAL-Level 예외 자습서에서 설명한 대로 아키텍처의 깊이 내에서 예외가 throw되면 다양한 계층을 통해 버블링됩니다. 프레젠테이션 계층에서 DetailsView의 ItemInserted 이벤트에서 예외가 발생했는지 확인할 수 있습니다. 이 이벤트 처리기는 ObjectDataSource InsertParameters의 값도 제공합니다. 따라서 예외가 있는지 확인하고 이 경우 ObjectDataSource의 brochurePath 매개 변수로 지정된 파일을 삭제하는 이벤트에 대한 ItemInserted 이벤트 처리기를 만들 수 있습니다.

Protected Sub NewCategory_ItemInserted _
    (sender As Object, e As DetailsViewInsertedEventArgs) _
    Handles NewCategory.ItemInserted
    
    If e.Exception IsNot Nothing Then
        ' Need to delete brochure file, if it exists
        If e.Values("brochurePath") IsNot Nothing Then
            System.IO.File.Delete(Server.MapPath _
                (e.Values("brochurePath").ToString()))
        End If
    End If
End Sub

요약

이진 데이터를 포함하는 레코드를 추가하기 위한 웹 기반 인터페이스를 제공하기 위해 수행해야 하는 여러 단계가 있습니다. 이진 데이터가 데이터베이스에 직접 저장되는 경우 아키텍처를 업데이트하고 이진 데이터가 삽입되는 경우를 처리하기 위한 특정 메서드를 추가해야 할 수 있습니다. 아키텍처가 업데이트되면 다음 단계는 삽입 인터페이스를 만드는 것입니다. 이 인터페이스는 각 이진 데이터 필드에 대한 FileUpload 컨트롤을 포함하도록 사용자 지정된 DetailsView를 사용하여 수행할 수 있습니다. 그런 다음 업로드된 데이터를 웹 서버의 파일 시스템에 저장하거나 DetailsView의 ItemInserting 이벤트 처리기에서 데이터 원본 매개 변수에 할당할 수 있습니다.

이진 데이터를 파일 시스템에 저장하려면 데이터를 데이터베이스에 직접 저장하는 것보다 더 많은 계획이 필요합니다. 한 사용자의 업로드가 다른 를 덮어쓰지 않도록 하려면 명명 체계를 선택해야 합니다. 또한 데이터베이스 삽입이 실패하는 경우 업로드된 파일을 삭제하려면 추가 단계를 수행해야 합니다.

이제 브로셔와 그림을 사용하여 시스템에 새 범주를 추가할 수 있지만 기존 범주의 이진 데이터를 업데이트하는 방법 또는 삭제된 범주에 대한 이진 데이터를 올바르게 제거하는 방법은 아직 살펴보지 않았습니다. 다음 자습서에서는 이 두 topics 살펴보겠습니다.

행복한 프로그래밍!

저자 정보

7개의 ASP/ASP.NET 책의 저자이자 4GuysFromRolla.com 창립자인 Scott Mitchell은 1998년부터 Microsoft 웹 기술로 작업해 왔습니다. Scott은 독립 컨설턴트, 트레이너 및 작가로 일합니다. 그의 최신 책은 샘스 티치 유어셀프 ASP.NET 24시간 만에 2.0입니다. 그는 에서mitchell@4GuysFromRolla.com 또는 에서 찾을 http://ScottOnWriting.NET수있는 자신의 블로그를 통해 도달 할 수 있습니다.

특별 감사

이 자습서 시리즈는 많은 유용한 검토자가 검토했습니다. 이 자습서의 수석 검토자는 데이브 가드너, 테레사 머피, 버나데트 리였습니다. 예정된 MSDN 문서를 검토하는 데 관심이 있으신가요? 그렇다면 에 줄을 놓습니다 mitchell@4GuysFromRolla.com.