다음을 통해 공유


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

작성자: 스콧 미첼

PDF 다운로드

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

소개

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

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

이 새 웹 페이지를 만들기 전에 아키텍처를 업데이트해야 합니다. CategoriesTableAdapter s main 쿼리는 열을 검색 Picture 하지 않습니다. 결과적으로 자동 생성된 Insert 메서드에는 , CategoryName, 및 Description 필드에 대한 BrochurePath입력만 있습니다. 따라서 TableAdapter에서 네 개의 Categories 필드를 모두 묻는 추가 메서드를 만들어야 합니다. CategoriesBLL Business Logic Layer의 클래스도 업데이트해야 합니다.

1단계: 메소드 추가InsertWithPictureCategoriesTableAdapter

CategoriesTableAdapter 자습서에서 뒷면을 만들 때 기본 쿼리를 기반으로 , INSERT, 및 UPDATE 문을 자동으로 생성DELETE하도록 구성했습니다. 또한 TableAdapter에 , 및 Insert. 메서드를 UpdateDelete생성한 DB Direct 접근 방식을 사용하도록 지시했습니다. 이러한 메서드는 자동 생성된 INSERT, UPDATE, 및 DELETE 문을 실행하며, 결과적으로 주 쿼리에서 반환된 열을 기반으로 입력 매개 변수를 허용합니다. 파일 업로드 자습서에서는 열을 사용하도록 s main 쿼리를 보강했습니다 CategoriesTableAdapterBrochurePath.

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

비고

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

이러한 잠재적인 골칫거리를 방지하려면 자동 생성된 SQL 문을 사용자 지정하는 대신 TableAdapter에 대한 새 메서드를 만들도록 합니다. 라는 이 InsertWithPicture메서드는 , CategoryName, DescriptionBrochurePath 열에 대한 Picture값을 수락하고 네 개의 값을 모두 새 레코드에 저장하는 문을 실행합니다INSERT.

Typed DataSet를 열고 디자이너에서 s 헤더를 마우스 오른쪽 단추로 클릭하고 CategoriesTableAdapter 상황에 맞는 메뉴에서 Add Query를 선택합니다. 그러면 TableAdapter 쿼리 구성 마법사가 시작되며, 이 마법사는 TableAdapter 쿼리가 데이터베이스에 액세스하는 방법을 묻는 것으로 시작합니다. SQL 문 사용을 선택하고 다음을 클릭합니다. 다음 단계에서는 생성할 쿼리 형식을 묻는 메시지를 표시합니다. 테이블에 새 레코드를 Categories 추가하는 쿼리를 만들고 있으므로 INSERT를 선택하고 Next를 클릭합니다.

INSERT 옵션을 선택합니다.

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

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

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

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

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

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

2단계: 비즈니스 로직 계층 업데이트

프레젠테이션 계층은 데이터 액세스 계층으로 직접 이동하기 위해 우회하는 대신 비즈니스 논리 계층과만 인터페이스해야 하므로 방금 만든InsertWithPicture DAL 메서드를 호출하는 BLL 메서드를 만들어야 합니다(). 이 자습서에서는 명명된 클래스에서 CategoriesBLL 3개의 InsertWithPicture s와 배열을 string 입력으로 허용하는 메서드를 byte 만듭니다. string 입력 매개 변수는 범주 이름, 설명 및 브로슈어 파일 경로에 byte 대한 것이고 배열은 범주 그림의 이진 콘텐츠에 대한 것입니다. 다음 코드에서 볼 수 있듯이 이 BLL 메서드는 해당 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);
}

비고

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

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

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

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

DisplayOrDownload.aspx에서 선언적 구문을 복사하여 UploadInDetailsView.aspx로 붙여넣기

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

선언적 구문과 GenerateBrochureLink 메서드를 UploadInDetailsView.aspx 페이지에 복사한 후 브라우저를 통해 페이지를 보고 모든 것이 올바르게 복사되었는지 확인합니다. 브로셔를 다운로드할 수 있는 링크와 범주 그림이 포함된 8개의 범주를 나열하는 GridView가 표시되어야 합니다.

이제 이진 데이터와 함께 각 범주를 볼 수 있습니다

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

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

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

먼저 ObjectDataSource의 스마트 태그에서 데이터 원본 구성 링크를 클릭합니다. 첫 번째 화면에는 데이터 소스가 작동하도록 구성된 개체 가 CategoriesBLL표시됩니다. 이 설정을 as-is 상태로 두고 다음을 클릭하여 데이터 메서드 정의 화면으로 이동합니다. 삽입 탭으로 이동하여 드롭다운 목록에서 방법을 선택합니다 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가 자동으로 새로 고쳐지고 새 범주가 표시됩니다.

먼저 도구 상자에서 GridView 위의 디자이너로 DetailsView를 끌어 해당 ID 속성을 설정하고 속성 NewCategory 값을 지웁니다 HeightWidth. DetailsView의 스마트 태그에서 기존 CategoriesDataSource 태그에 바인딩한 다음 삽입 사용 확인란을 선택합니다.

CategoryID 속성이 NewCategory로 설정되고 Height 및 Width 속성 값이 비어 있고 삽입 사용 확인란이 선택된 DetailsView가 열려 있는 스크린샷을 보여 줍니다.

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

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

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

DetailsView에서 BoundField를 NumberOfProducts 완전히 제거한 다음 및 HeaderText BoundFields의 CategoryName 속성을 각각 Category 및 Brochure로 업데이트합니다BrochurePath. 다음으로, BoundField를 BrochurePath TemplateField로 변환하고 그림에 대한 새 TemplateField를 추가하여 이 새 TemplateField에 HeaderText Picture 값을 지정합니다. TemplateField와 CommandField 사이에 Picture 있도록 TemplateField를 BrochurePath 이동합니다.

TemplateField, Picture 및 HeaderText가 강조 표시된 필드 창의 스크린샷입니다.

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

필드 편집 대화 상자를 통해 BoundField를 BrochurePath TemplateField로 변환한 경우 TemplateField에는 , ItemTemplate, 및 EditItemTemplate가 포함됩니다InsertItemTemplate. 그러나 필요한 것은 오직 하나 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가 포함되어 있지만 TemplateField에는 Picture 템플릿이 포함되어 있지 않습니다. FileUpload 컨트롤을 사용하려면 이 두 TemplateField를 InsertItemTemplate 업데이트해야 합니다.

DetailsView의 스마트 태그에서 템플릿 편집 옵션을 선택한 다음 드롭다운 목록에서 TemplateField를 BrochurePath 선택합니다InsertItemTemplate. TextBox를 제거한 다음 도구 상자에서 템플릿으로 FileUpload 컨트롤을 끌어 옵니다. FileUpload 컨트롤을 IDBrochureUpload로 설정합니다. 마찬가지로 FileUpload 컨트롤을 Picture TemplateField s InsertItemTemplate에 추가합니다. 이 FileUpload 컨트롤을 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와 같은 특정 유형의 이미지 파일만 허용합니까? 다른 파일 형식을 허용하려면 파일 형식을 캡처하는 열을 포함하도록 스키마를 확장하여 Categories 이 형식을 를 Response.ContentType통해 DisplayCategoryPicture.aspx 클라이언트로 보낼 수 있어야 합니다. 이러한 열이 없기 때문에 사용자가 특정 이미지 파일 형식만 제공하도록 제한하는 것이 현명합니다. Categories 테이블의 기존 이미지는 비트맵이지만 JPG는 웹을 통해 제공되는 이미지에 더 적합한 파일 형식입니다.

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

비고

이상적으로는 및 CategoryName BoundFields가 Description TemplateFields로 변환되고 삽입 인터페이스가 사용자 지정됩니다. Description 예를 들어, 삽입 인터페이스는 여러 줄로 된 텍스트 상자를 통해 더 적합할 수 있습니다. 또한 열에는 CategoryName 값이 허용되지 않으므로 NULL 사용자가 새 범주 이름에 대한 값을 제공하도록 RequiredFieldValidator를 추가해야 합니다. 이 단계는 독자의 연습 과제로 남겨둡니다. 데이터 수정 인터페이스 보강에 대한 자세한 내용은 Customizing the Data Modification Interface 를 다시 참조하십시오.

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

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

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

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

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

비고

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

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

다음 코드에서는 File.Exists(path) 메서드를 사용하여 지정된 파일 이름을 가진 파일이 이미 있는지 확인합니다. 이 경우 충돌이 발견되지 않을 때까지 브로슈어의 새 파일 이름을 계속 시도합니다.

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

유효한 파일 이름을 찾으면 파일을 파일 시스템에 저장해야 하며 이 파일 이름이 데이터베이스에 기록되도록 ObjectDataSource 값을 brochurePath``InsertParameter 업데이트해야 합니다. 파일 업로드 자습서에서 다시 보았듯이 FileUpload 컨트롤 s SaveAs(path) 메서드를 사용하여 파일을 저장할 수 있습니다. ObjectDataSource s 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 이벤트의 picture ObjectDataSource s ItemInserting 매개 변수에 할당해야 합니다. 그러나 이 할당을 하기 전에 먼저 업로드된 사진이 다른 이미지 유형이 아닌 JPG인지 확인해야 합니다. 6단계에서와 같이 업로드된 사진의 파일 확장자를 사용하여 유형을 확인하겠습니다.

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

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

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

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

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

CompleteItemInserting이벤트 처리기

완전성을 위해 다음은 전체 이벤트 처리기입니다 ItemInserting .

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

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

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

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

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

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

새 범주의 그림이 표시되지 않습니다.

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

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

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

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

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

새 범주에 대한 JPG 이미지가 올바르게 렌더링됩니다.

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

9단계: 예외 상황에서 브로셔 삭제

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

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

ASP.NET 페이지에서 BLL 및 DAL-Level 예외 처리 자습서에서 이전에 설명한 것처럼 아키텍처의 깊은 곳에서 예외가 발생하면 다양한 계층을 통해 버블링됩니다. 프레젠테이션 계층에서 DetailsView s ItemInserted 이벤트에서 예외가 발생했는지 확인할 수 있습니다. 이 이벤트 처리기는 ObjectDataSource s InsertParameters의 값도 제공합니다. 따라서 예외가 있는지 확인하고, 예외가 있는 경우 ObjectDataSource s ItemInserted 매개 변수로 지정된 파일을 삭제하는 이벤트에 대한 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()));
    }
}

요약

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

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

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

행복한 프로그래밍!

작성자 정보

7개의 ASP/ASP.NET 책의 저자이자 4GuysFromRolla.com 창립자인 Scott Mitchell은 1998년부터 Microsoft 웹 기술을 연구해 왔습니다. Scott은 독립 컨설턴트, 트레이너 및 작가로 일합니다. 그의 최신 책은 Sams Teach Yourself ASP.NET 2.0 in 24 Hours입니다. 그에게 mitchell@4GuysFromRolla.com으로 연락할 수 있습니다.

특별히 감사드립니다.

이 자습서 시리즈는 많은 유용한 검토자가 검토했습니다. 이 자습서의 수석 검토자는 Dave Gardner, Teresa Murphy 및 Bernadette Leigh였습니다. 예정된 MSDN 문서를 검토하는 데 관심이 있으신가요? 그렇다면 mitchell@4GuysFromRolla.com으로 메시지를 보내 주세요.