五十四添加新记录时包含一个文件上传选项.docx
- 文档编号:11459814
- 上传时间:2023-03-01
- 格式:DOCX
- 页数:27
- 大小:603.99KB
五十四添加新记录时包含一个文件上传选项.docx
《五十四添加新记录时包含一个文件上传选项.docx》由会员分享,可在线阅读,更多相关《五十四添加新记录时包含一个文件上传选项.docx(27页珍藏版)》请在冰豆网上搜索。
五十四添加新记录时包含一个文件上传选项
在ASP.NET2.0中操作数据之五十四:
添加新记录时包含一个文件上传选项
作者:
heker2007字体:
[增加 减小]类型:
转载时间:
2016-05-17 我要评论
上篇文章主要介绍了,ASP.NET2.0中如何显示二进制数据,本文主要介绍如何将图片上传,转换成二进制数据保存在数据库中。
导言:
在前面2节教程,我们探讨了如何使用FileUpload控件从客户端向服务器上传文件,以及如何在数据Web控件里显示二进制数据。
在本节,我们将创建一个web页面以添加新的种类。
除了为类的name和description属性添加TextBoxes控件外,我们还要在页面上添加2个FileUpload控件——一个用来上传新类的图片,另一个用来上传类的小说明册子。
上传的图片将直接存储在新记录的Picture列。
与此相反,小册子将存储在~/Brochures文件夹,同时将文件路径存储在新记录的BrochurePath列。
在创建页面之前,我们需要更新体系结构。
由于CategoriesTableAdapter的主查询并不返回Picture列,因此自动生产的Insert方法只包含了CategoryName,Description和BrochurePath列。
我们需要在TableAdapter里创建新的方法以包括Categories的4个列。
同时业务逻辑层的的CategoriesBLL类也需要更新。
第1步:
在CategoriesTableAdapter添加一个InsertWithPicture方法
在前面的教程《创建一个数据访问层》里我们创建了CategoriesTableAdapter,并设置其自动生成了基于主查询的INSERT,UPDATE和DELETE命令。
此外,我们设置该TableAdapter启用DBDirect方法,它将创建Insert,Update和Delete方法。
这些方法执行自动生成的INSERT,UPDATE和DELETE命令,自然而然的,其接受的输入参数基于主查询所返回的那些列。
在教程《使用FileUpload上传文件》里,我们扩展了CategoriesTableAdapter的主查询以包含BrochurePath列。
因为CategoriesTableAdapter的主查询并为引用Picture,在添加新记录或更新记录时不能涉及Picture值。
为了获取Picture信息,我们要么在TableAdapter里创建一个新方法以插入Picture的二进制数据;要么定制自动生成的INSERT命令。
但定制自动生成的INSERT命令有一个风险,即定制的INSERT命令有可能被向导覆盖。
比如,假设我们定制INSERT命令使用Picture列,更新TableAdapter的Insert方法,使之多包含一个对应picture二进制数据的参数。
然后在业务逻辑层创建一个方法使用该DAL方法,再在表现层调用该业务逻辑层方法。
现在一切工作正常,但当下一次在TableAdapter设置向导里设置TableAdapter完成后,我们定制的INSERT命令马上就会被向导重写,回归到定制前的状态。
其结果是我们的代码将无法编译!
注意:
如果使用存储过程而不用SQL语句的话,就不存在这个问题。
在以后的教程里,我们将探讨在数据访问层用存储过程替代SQL语句。
为避免这个头痛的问题,我们为TableAdapter添加新的方法,而不定制自动生成的SQL命令。
我们为添加的方法命名为InsertWithPicture,它接受CategoryName,Description,BrochurePath和Picture值;执行INSERT命令将上述值添加进一条记录。
在CategoriesTableAdapter的顶部点右键,选择“添加查询”。
进入TableAdapter查询设置向导,首先询问我们TableAdapter查询如何访问数据库,选择“使用SQL语句”,点Next,因为我们要为表Categories添加新记录,选“INSERT”,点Next。
图1:
选“INSERT”选项
现在,我们需要指定INSERTSQL语句。
向导自动地生成一个基于主查询的INSERT语句。
此时,它只插入CategoryName,Description和BrochurePath值。
对其更新,包括Picture列和参数@Picture,如下:
?
1
2
3
4
INSERTINTO[Categories]
([CategoryName],[Description],[BrochurePath],[Picture])
VALUES
(@CategoryName,@Description,@BrochurePath,@Picture)
最后,向导要我们为方法命名,取名为InsertWithPicture,点Finish。
图2:
为新方法命名为InsertWithPicture
第2步:
更新业务逻辑层
由于一般来说表现层将引用业务逻辑层,而不是绕过它直接引用数据访问层,我们需要创建一个业务逻辑层方法,以调用刚才创建的数据访问层方法(InsertWithPicture),本节,我们在CategoriesBLL里创建一个名为InsertWithPicture方法,它接受3个字符串和一个byte数组,字符串参数对应name,description和brochure文件地址;byte数组对应于图片的二进制内容。
就像下面的代码所显示的那样,BLL方法调用相应DAL方法:
?
1
2
3
4
5
6
7
[System.ComponentModel.DataObjectMethodAttribute
(System.ComponentModel.DataObjectMethodType.Insert,false)]
publicvoidInsertWithPicture(stringcategoryName,stringdescription,
stringbrochurePath,byte[]picture)
{
Adapter.InsertWithPicture(categoryName,description,brochurePath,picture);
}
注意:
在为BLL添加InsertWithPicture方法前,确保已经保存了数据集(TypedDataSet),因为CategoriesTableAdapter类的代码是基于TypedDataSet自动生成的。
如果最开始没有把对TypedDataSet所进行的修改保存的话,Adapter属性将不认同InsertWithPicture方法。
第3步:
列出现有的种类及其二进制数据
本教程我们将创建一个页面,允许用户添加新的类,包含其图片和说明小册子。
在上一节,我们用一个包含TemplateField和ImageField的GridView控件来展示每个类的名称、描述,并包含一个下载说明小册子的链接。
在本教程,我们实现相同的功能,创建一个页面,即展示现有的类,还可以添加新的类。
打开BinaryData文件夹的DisplayOrDownload.aspx页面,切换到源模式,复制GridView和ObjectDataSource控件的声明代码,粘贴在UploadInDetailsView.aspx页面的 Content>元素里。 同时不要忘记将后台代码类的GenerateBrochureLink方法拷贝到UploadInDetailsView.aspx的后台代码类里。 图3: 将DisplayOrDownload.aspx页面的声明代码拷贝到页面UploadInDetailsView.aspx 完成以后,在浏览器里查看该页面,确保一切正常。 GridView控件里列出了8个类,每个类包含一张图片以及一个下载说明小册子的链接。 图4: 你应该看到每个类及其相应二进制数据 第4步: 设置CategoriesDataSource以支持添加功能 那个ID为Categories的GridView控件所使用的名为CategoriesDataSource的ObjectDataSource控件目前还不支持添加数据。 为实现该功能,我们要设置该控件的Insert方法引用类CategoriesBLL的某个方法。 具体的讲,我们要用到在第2步里添加的InsertWithPicture方法。 在ObjectDataSource控件的智能标签里,点“设置数据源”。 照原样一直点到“DefineDataMethods”界面。 再点INSERT标签,从下拉列表里选方法“InsertWithPicture”,点Finish完成设置。 图5: 设置ObjectDataSource控件使用InsertWithPicture方法 注意: 当完成设置后,VisualStudio会问你是否“刷新FieldsandKeys”,选择No,因为如果选Yes的话,将重新构造dataWebcontrolsfields,那样将重写所有我们已经定制好的列(field)。 完成设置后,ObjectDataSource控件将会为InsertMethod属性赋值,同时包含一个 ? 1 2 3 4 5 6 7 8 9 10 ObjectDataSourceID="CategoriesDataSource"runat="server" OldValuesParameterFormatString="original_{0}"SelectMethod="GetCategories" TypeName="CategoriesBLL"InsertMethod="InsertWithPicture"> ParameterName="categoryName"Type="String"/> ParameterName="description"Type="String"/> ParameterName="brochurePath"Type="String"/> ParameterName="picture"Type="Object"/> ObjectDataSource> 第5步: 创建一个插入界面 在教程16《概述插入、更新和删除数据》里我们谈到,当DetailsView控件的数据源控件支持添加功能时,便可以启用DetailsView内置的添加界面。 让我们在页面上添加一个DetailsView控件,置于GridView控件之上,并处于添加模式。 当在DetailsView控件里添加一个新种类时,其下的GridView控件将自动发生刷新,并将刚添加的类显示出来。 从工具箱拖一个DetailsView控件到页面,置于GridView之上,设其ID为NewCategory,清空其Height和Width属性。 其智能标签里,设置它绑定到名为CategoriesDataSource的数据源,并启用“插入”功能。 图6: 将DetailsView控件绑定到CategoriesDataSource,并启用插入功能。 为使DetailsView呈现为插入界面,设其DefaultMode属性为Insert 我们注意到,尽管DetailsView控件有5个BoundFields——CategoryID,CategoryName,Description,NumberOfProducts和BrochurePath,但插入界面并不包含CategoryID,因为CategoryID列的InsertVisible属性为false。 为什么会显示这4个列呢? 因为ObjectDataSource调用的GetCategories()方法返回的就是这些列。 当添加新类时,我们不希望用户为NumberOfProducts列指定值,此外,我们还希望让用户为新类上传图片和相关的PDF小册子。 在DetailsView里将NumberOfProducts列完成删除,再分别CategoryName列和BrochurePath列的HeaderText属性设置为“Category”和“Brochure”。 将BrochurePath转换为TemplateField,再添加一个TemplateField,设其HeaderText属性为“Picture”,把它放置在BrochurePath列和CommandField列之间。 图7: 将DetailsView控件绑定到CategoriesDataSource,并启用插入功能(注: 图片说明有误) 当你在“编辑列”对话框里将BrochurePathBoundField转换为一个TemplateField后,该TemplateField将包含3个模板: ItemTemplate,EditItemTemplate和InsertItemTemplate,由于我们只需要InsertItemTemplate模板,将另外2个模板删除。 如此,你的DetailsView控件的声明代码看起来应该像下面的这样: ? 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 DetailsViewID="NewCategory"runat="server"AutoGenerateRows="False" DataKeyNames="CategoryID"DataSourceID="CategoriesDataSource" DefaultMode="Insert"> BoundFieldDataField="CategoryID"HeaderText="CategoryID" InsertVisible="False"ReadOnly="True" SortExpression="CategoryID"/> BoundFieldDataField="CategoryName"HeaderText="Category" SortExpression="CategoryName"/> BoundFieldDataField="Description"HeaderText="Description" SortExpression="Description"/> TemplateFieldHeaderText="Brochure"SortExpression="BrochurePath"> TextBoxID="TextBox1"runat="server" Text='<%#Bind("BrochurePath")%>'> TextBox> TemplateField> TemplateFieldHeaderText="Picture"> TemplateField> CommandFieldShowInsertButton="True"/> DetailsView> 为Brochure和PictureFields添加FileUpload控件 当前,BrochurePathTemplateField的InsertItemTemplate模板包含一个TextBox,而PictureTemplateField并不包含任何的模板,我们为这2个TemplateField的InsertItemTemplate模板模板添加FileUpload控件。 从DetailsView控件的智能标签选择“编辑模板”,从下拉列表选择BrochurePathTemplateField的InsertItemTemplate模板,将模板里的TextBox删除,从工具箱拖一个FileUpload控件到页面,设其ID为BrochureUpload。 类似的,为PictureTemplateField的InsertItemTemplate模板添加一个ID为PictureUpload的FileUpload控件。 图8: 在InsertItemTemplate模板里添加一个FileUpload控件 完成添加后,这2个TemplateField的声明代码应该和下面的差不多: ? 1 2 3 4 5 6 7 8 9 10 TemplateFieldHeaderText="Brochure"SortExpression="BrochurePath"> FileUploadID="BrochureUpload"runat="server"/> TemplateField> TemplateFieldHeaderText="Picture"> FileUploadID="PictureUpload"runat="server"/> TemplateField> 当用户添加一个新类时,我们希望确保上传的图片和说明小册子是恰当的文件类型。 对说明小册子,必须是PDF类型;对图片,我们需要用户上传一个image文件。 那是不是image文件必须是某个特定的类型呢,比如GIF或JPG? 考虑到其它不同类型的文件,我们需要扩展表Categories的列以包含这些类型的文件,同时我们可以在页面DisplayCategoryPicture.aspx里通过Response.ContentType将这些文件发送到客户端。 由于表Categories现在并没有这样的列,我们只有限制用户上传指定为某种类型的image文件。 表Categories里现有的images为位图,不过使用JPG类型或许更恰当。 当用户上传的文件类型不正确时,我们将取消插入操作,并显示一个提示信息。 在DetailsView控件下添加一个LabelWeb控件,设ID为UploadWarning,清除Text属性,设CssClass属性为“Warning”,再将Visible和EnableViewState属性都设为false。 WarningCSS定义在Styles.css里,作用是将文字显示为粗斜体,红色大号字。 注意: 最理想的情况是将CategoryName和DescriptionBoundFields都转换为TemplateFields,达到定制插入界面的目的。 比如,对Description插入界面来说,使用一个允许分行的文本框或许更好;对CategoryName插入界面,因为CategoryName不允许为NULL值,我们应该添加一个RequiredFieldValidator控件,以确保输入类的名称。 这些步骤都留给读者做练习,更深入的探讨请参考前面的教程之20《定制数据修改界面》 第6步: 将上传的小册子保存在服务器的文件系统 但用户键入相关的类别信息,点Insert按钮后,发生页面回传,接着发生一连串的插入流程。 首先,DetailsView控件的ItemInsertingevent事件发生;接着,调用ObjectDataSource控件的Insert()方法,它将导致Categories表添加新记录;最后,发生DetailsView控件的ItemInsertedevent事件。 在调用ObjectDataSource控件的Insert()方法以前,我们必须确保用户已经上传了恰当的文件并保存在服务器的文件系统。 为此,我们为DetailsView控件的ItemInserting事件创建一个事件处理器,添加如下的代码: ? 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 //ReferencetheFileUploadcontrol FileUploadBrochureUpload= (FileUpload)NewCategory.FindControl("BrochureUpload"); if(BrochureUpload.HasFile) { //MakesurethataPDFhasbeenuploaded if(string.Compare(System.IO.Path.GetExtension (BrochureUpload.FileName),".pdf",true)! =0) { UploadWarning.Text= "OnlyPDFdocumentsmaybeusedforacategory'sbrochure."; UploadWarning.Visible=true; e.Cancel=true; return; } } 代码首先引用DetailsView控件模板里名为BrochureUpload的FileUpload控件,如果已经上传了文件,就检查FileUpload控件的extension是否为“.PDF”,如果不是则取消插入操作并退出。 注意: 通过检查文件的扩展名(extension)来确保用户上传的为PDF文件的做法并不是万全之策。 比如,可能用户的确上传的是PDF文件,只不过其扩展名为.Brochure;或者用户提供的并不是PDF文件,却使用.pdf的扩展名。 保险的做法是通过编程对文件内容做最后一次检查。 如此一来,虽然彻底,但稍嫌过头(overkill)。 在绝大多数情况下,检查文件扩展名就已经足够了。 就像在教程《使用FileUpload上传文件》里讨论的那样,将文件保存在文件系统里时要特别小心,以免覆盖别人上传的文件。 本节,我们尝试对上传文件使用一个已经使用的名字,在名字末尾添加一个数字,以示区别。 举例,如果在文件夹~/Brochures里存在一个名为Meats.pdf的文件,上传文件时我们取名为Meats-1.pdf,如果文件夹里恰好也存在一个Meats-1.pdf文件,我们就取名为Meats-2.pdf,以此类推,直到文件名唯一为止。 下面的代码使用File.Exists(path)方法来判断是否已经存在同名文件,如果存在,就重新命名,直到名字唯一为止: ? 1 2 3 4 5 6 7 8 9 10 11 12 13 conststringBrochureDirectory="~/Brochures/"; stringbrochurePath=BrochureDirectory+BrochureUpload.FileName; stringf
- 配套讲稿:
如PPT文件的首页显示word图标,表示该PPT已包含配套word讲稿。双击word图标可打开word文档。
- 特殊限制:
部分文档作品中含有的国旗、国徽等图片,仅作为作品整体效果示例展示,禁止商用。设计者仅对作品中独创性部分享有著作权。
- 关 键 词:
- 五十四 添加 记录 包含 一个 文件 上传 选项