Home>

preface:

In the previous 2 tutorials, we discussed how to use the fileupload control to upload files from the client to the server.And how to display binary data in the data web control.

In this section, we will create a web page to add new categories.In addition to adding textboxes controls to the name and description properties of the class, we also need to add two fileupload controls to the page-one to upload pictures of the new classAnother small booklet for uploading classes.The uploaded picture will be stored directly in the picture column of the new record. opposite of this,Booklets will be stored in the ~/brochures folder, along with the file path in the newly recorded brochurepath column.

Before creating the page,We need to update the architecture.Since the main query of the categoriestableadapter does not return the picture column, the automatically produced insert method contains only the categoryname, description and brochurepath columns. We need to create a new method in the tableadapter to include the 4 columns of categories. At the same time, the categoriesbll class of the business logic layer also needs to be updated.

Step 1:Add an insertwithpicture method to the categoriestableadapter

In the previous tutorial "Create a data access layer》 In we created categoriestableadapter and set it to automatically generate insert, update, and delete commands based on the main query. In addition, we set the tableadapter to enable the db direct method, which will create insert, update, and delete methods. These methods execute the automatically generated insert, update, and delete commands. Naturally,It accepts input parameters based on those columns returned by the main query.In the tutorial "Upload files using fileuploadIn》, we extended the main query of the categoriestableadapter to include the brochurepath column.

Because the main query of the categoriestableadapter is a reference picture, the picture value cannot be involved when adding new records or updating records. In order to get the picture information, we either create a new method in the tableadapter to insert the binary data of the picture;Either customize the automatically generated insert command. But there is a risk in customizing the automatically generated insert command,That is, the custom insert command may be overwritten by the wizard.For example, suppose we customize the insert command to use the picture column and update the insert method of the tableadapter to include a parameter corresponding to the binary data of the picture.Then create a method in the business logic layer to use the dal method, and then call the business logic layer method in the presentation layer.Now everything works fine,But the next time we set up the tableadapter in the tableadapter setup wizard, our custom insert command will be rewritten by the wizard immediately.Return to the state before customization.As a result, our code will not compile!

Note:If you use stored procedures instead of sql statements,This problem does not exist.In future tutorials,We will explore replacing sql statements with stored procedures at the data access layer.

To avoid this headache,We add new methods for tableadapter,Without customizing the automatically generated sql command. We named the added method insertwithpicture, which accepts categoryname, description, brochurepath and picture values;execute the insert command to add the above values ​​to a record.

Right-click on the top of the categoriestableadapter,Select Add Query. Enter the tableadapter query setup wizard,First ask us tableadapter query how to access the database,Select "Use SQL statement" and click next, because we want to add new records for the table categories,Select "insert" and click next.

Figure 1:Selecting the "insert" option

Now, we need to specify the insert sql statement. The wizard automatically generates an insert statement based on the main query. At this point, it only inserts the categoryname, description, and brochurepath values. Update it,Include the picture column and the parameter @picture as follows:

insert into [categories]
 ([categoryname], [description], [brochurepath], [picture])
values
 (@categoryname, @description, @brochurepath, @picture)

Finally, the wizard asks us to name the method,Name it insertwithpicture and click finish.

Figure 2:Name the new method insertwithpicture

Step 2:Update the business logic layer

As the presentation layer will generally reference the business logic layer,Instead of bypassing it directly to reference the data access layer,We need to create a business logic layer method,In order to call the data access layer method (insertwithpicture) just created, in this section, we create a method named insertwithpicture in categoriesbll, which accepts 3 strings and a byte array, and the string parameters correspond to the name, description and brochure file addressThe byte array corresponds to the binary content of the picture.As the code below shows,The bll method calls the corresponding dal method:

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

Note:Before adding the insertwithpicture method to bll, make sure that the data set (typed dataset) has been saved, because the code of the categoriestableadapter class is automatically generated based on the typed dataset.If the changes made to the typed dataset were not saved initially,The adapter property will not recognize the insertwithpicture method.

Step 3:List existing species and their binary data

In this tutorial we will create a page,Allow users to add new classes,Contains its picture and description booklet.In the previous section,We use a gridview control with templatefield and imagefield to display the name, description of each class, and a link to download the instruction booklet.In this tutorial,We implement the same function,Create a page,I.e. showing existing classes,You can also add new classes.

Open the displayordownload.aspx page of the binarydata folder, switch to source mode,Copy the declaration code of the gridview and objectdatasource controls,Paste in theelement of the uploadindetailsview.aspx page. At the same time don't forget to copy the generatebrochurelink method of the background code class to the background code class of uploadindetailsview.aspx.

Figure 3:Copy the declaration code of the displayordownload.aspx page to the page uploadindetailsview.aspx

After completion,View the page in your browser,Make sure everything works.The gridview control lists eight classes, each of which contains an image and a link to download an instruction booklet.

Figure 4:You should see each class and its corresponding binary data

Step 4:Set categoriesdatasource to support adding features

ObjectThe objectdatasource control named categoriesdatasource used by the gridview control whose id is categories does not yet support adding data.To achieve this function,We want to set the insert method of the control to reference a method of the categories categorybll.Specifically,We will use the insertwithpicture method we added in step 2.

In the smart tag of the objectdatasource control,Click "Set Data Source". Click on the "define data methods" screen as it is. Then click the insert tab, select the method "insertwithpicture" from the drop-down list, and click finish to complete the setting.

Figure 5:Set the objectdatasource control to use the insertwithpicture method

Note:When the setting is completed,visual studio will ask you whether to "refresh fields and keys", select no, because if you choose yes, the data web controls fields will be reconstructed, which will rewrite all the fields we have customized.

After setting up,The objectdatasource control will assign a value to the insertmethod property.Contains an<insertparameters>as shown in the following declaration code:

<asp:objectdatasource 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>

Step 5:Create an Insert Interface

In Tutorial 16 "Overview of inserting, updating, and deleting dataWe talked aboutWhen the data source control of the detailsview control supports adding features,You can enable the built-in add interface of detailsview.Let's add a detailsview control on the page, placed on top of the gridview control,And in add mode.When adding a new category to the detailsview control,The gridview control under it will automatically refresh,And show the class just added.

Drag a detailsview control from the toolbox to the page,Place it on the gridview, set its id to newcategory, and clear its height and width properties. In its smart tag,Set it to bind to a data source named categoriesdatasource,And enable the "Insert" feature.

Figure 6:Bind the detailsview control to the categoriesdatasource and enable the insert function.

In order to render the detailsview as an insertion interface,Set its defaultmode attribute to insert

We have noticed,Although the detailsview control has five boundfields—categoryid, categoryname, description, numberofproducts, and brochurepath, the insert interface does not include categoryid because the insertvisible property of the categoryid column is false. Why are these 4 columns displayed?Because the getcategories () method called by objectdatasource returns these columns.When adding a new class,We don't want users to specify a value for the numberofproducts column,In addition, we want to let users upload pictures and related pdf brochures for the new category.

Delete the numberofproducts column in detailsview.Set the headertext attribute of the categoryname column and the brochurepath column to "category" and "brochure", respectively. Convert brochurepath to templatefield, add another templatefield, set its headertext property to "picture", and place it between the brochurepath column and the commandfield column.

Figure 7:Bind the detailsview control to the categoriesdatasource and enable the insert function (note:the picture description is incorrect)

When you convert the brochurepath boundfield into a templatefield in the Edit Column dialog box, the templatefield will contain 3 templates:itemtemplate, edititemtemplate, and insertitemtemplate. Since we only need the insertitemtemplate template, delete the other two templates.So, the declaration code of your detailsview control should look like this:

<asp:detailsview 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 runat="server"
   text="<%#bind (" brochurepath ")%>"></asp:textbox>
  </insertitemtemplate>
 </asp:templatefield>
 <asp:templatefield headertext="picture"></asp:templatefield>
 <asp:commandfield showinsertbutton="true" />
 </fields>
</asp:detailsview>

Add fileupload control for brochure and picture fields

Currently, the insertitemtemplate template of the brochurepath templatefield contains a textbox, and the picture templatefield does not contain any templates.We add a fileupload control to the insertitemtemplate template template of these two templatefields.

Select "Edit Template" from the smart tag of the detailsview control, select the insertitemtemplate template of the broochpath templatefield from the drop-down list, delete the textbox in the template, drag a fileupload control from the toolbox to the page,Let its id be brochureupload. Similarly, add a fileupload control with an id of pictureupload to the insertitemtemplate template of the picture templatefield.

Figure 8:Add a fileupload control to the insertitemtemplate template

After adding,The declaration code of these two templatefields should be similar to the following:

<asp:templatefield headertext="brochure" sortexpression="brochurepath">
 <insertitemtemplate>
 <asp:fileupload runat="server" />
 </insertitemtemplate>
</asp:templatefield>
<asp:templatefield headertext="picture">
 <insertitemtemplate>
 <asp:fileupload runat="server" />
 </insertitemtemplate>
</asp:templatefield>

When the user adds a new class,We want to make sure that uploaded pictures and brochures are the right file type.To the instruction booklet,Must be pdf;for images, we need users to upload an image file. Is the image file a certain type?Like gif or jpg?Considering other different types of files,We need to expand the columns of the table categories to include these types of files,At the same time, we can send these files to the client through response.contenttype in the displaycategorypicture.aspx page.Since the table categories do not have such columns now,We only restrict users from uploading image files designated as some type. The existing images in the categories are bitmaps, but using the jpg type may be more appropriate.

When the file type uploaded by the user is incorrect,We will cancel the insert operation,A message is displayed.Add a label web control under the detailsview control, set the id to uploadwarning, clear the text property, set the cssclass property to "warning", and set the visible and enableviewstate properties to false. warning css is defined in styles.css, which is used to display text in bold italics,Large red word.

Note:The ideal situation is to convert both categoryname and description boundfields to templatefields to achieve the purpose of customizing the insertion interface.For example, for the description insertion interface,It may be better to use a text box that allows branches;Insert interface for categoryname,Because categoryname is not allowed to be a null value, we should add a requiredfieldvalidator control to ensure the name of the input class.These steps are left as an exercise for the reader,For a more in-depth discussion, please refer to the previous 20Custom data modification interface》

Step 6:Save the uploaded brochure in the server's file system

But the user types the relevant category information,After clicking the insert button, a page postback occurs,Then a series of insertion processes occur.First, the itemsing event of the detailsview control occurs;Next, call the insert () method of the objectdatasource control, which will cause new records to be added to the categories table;Finally, an itemized event of the detailsview control occurs.

Before calling the insert () method of the objectdatasource control,We must ensure that users have uploaded the appropriate files and saved them on the server's file system.To this end, we create an event handler for the itemserting event of the detailsview control,Add the following code:

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

The code first references the fileupload control named brochureupload in the detailsview control template. If a file has been uploaded,Just check if the extension of the fileupload control is ".pdf", if not, cancel the insert operation and exit.

Note:It is not a good idea to check the extension of the file to ensure that the user uploads it as a pdf file.For example, maybe the user did upload a pdf file, but the extension is.brochure;or the user did not provide a pdf file, but used the .pdf extension.It is safe to programmatically check the contents of the file for the last time.As a result,Although thorough,But it's a little overkill. In the vast majority of cases,Checking the file extension is enough.

Like in the tutorial "Upload files using fileuploadAs discussed inTake special care when saving files in the file system,So as not to overwrite files uploaded by others.In this section, we try to use an already used name for the uploaded file.Add a number to the end of the name,To show the difference.For example, if there is a file named meats.pdf in the folder ~/brochures, when uploading the file we will call meats-1.pdf. If there is also a meats-1.pdf file in the folder, we will Named meats-2.pdf, and so on,Until the file name is unique.

The following code uses the file.exists (path) method to determine whether a file with the same name already exists.if it exists,Just rename it,Until the name is unique:

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

Once the unique file name is found,Save the file on the file system immediately,At the same time, update the value of the insertparameter parameter brochurepath of the objectdatasource control to write the file name to the database.Just like in the tutorial "Upload files using fileuploadI see the same inYou can use the saveas (path) method of the fileupload control to save the file.Use the e.values ​​collection to update the parameter datapath of the objectdatasource control.

//save the file to disk and set the value of the brochurepath parameter
brochureupload.saveas (server.mappath (brochurepath));
e.values ​​["brochurepath"]=brochurepath;

Step 7:Save the uploaded picture to the database

In order to save uploaded pictures in newly added records,We need to assign the picture parameter of the objectdatasource control with the uploaded data in the itemserting event of the detailsview control.However, before that,We need to make sure the uploaded file is jpg and not some other format.As discussed in step 6,We use the file extension to check its type.

Although the category table allows the picture column to be null, all categories should have a picture.On this page,We force users to provide pictures when adding records.The following code ensures that the image has been uploaded,And is the appropriate type.

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

This code should be placed before the code in step 6,If there is a problem with the uploaded file,The event handler ends before the file is saved to the file system.

Assuming there are no issues with the uploaded file,Then we use the following code to assign the data of the uploaded file to the parameter picture:

//set the value of the picture parameter
e.values ​​["picture"]=pictureupload.filebytes;

Complete itemserting event handler

Here is the complete code for the itemserting event handler:

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

Step 8:Update the displaycategorypicture.aspx page

Let's take a few minutes to test the insertion interface and itemserting event handler we created in the previous steps.View the uploadindetailsview.aspx page in your browser, try to add a class,Ignore picture or specify a non-jpg picture or non-pdf brochure.In either case,Will display an error message,And cancel the insert operation.

Figure 9:A warning message is displayed when the uploaded file is incorrect

The confirmation page asks to upload an image,We do not accept non-pdf or non-jpg files. Add a new category containing pictures in jpg format,Leave the brochure column blank. After clicking the insert button, the page returns,A new record will be added to the categories table,At the same time, the uploaded picture data is directly stored in the database.After the gridview control is updated,Display the newly added class.However, as shown in Figure 10,The class picture is not displayed correctly.

Figure 10:Pictures of the new category are not shown

The reason why the picture is not displayed is because the page displaycategorypicture.aspx used to return a picture of a specific class is set to handle bitmaps with an ole header.When the picture column data was returned to the client, the 78-byte header was stripped.And the uploaded jpg file has no ole header, so the necessary bytes have been removed from the binary data of the image.

Because there are both jpg files and bitmaps with ole headers in the categories,We need to adjust the page displaycategorypicture.aspx so that it strips the ole header from the original 8 classes, not the newly added classes.In later tutorials,We explore how to update the image file of an existing record and adjust all previous class images to jpg format. Now, in the page displaycategorypicture.aspx, we use the following code to strip the original 8 class ole headers.

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

After making the above modifications,The jpg image can now be displayed correctly in the gridview control.

Figure 11:The jpg image of the newly added class can be displayed correctly

Step 9:Delete the brochure file when an exception occurs

There is also a problem in saving uploaded files in the file system,That is, the data cannot be associated with the storage mode.When deleting a record,The corresponding files stored in the file system should also be deleted;Similarly, when adding records,The same is true. Assume these situations:When a user adds a new category,He specified a picture and a brochure.After clicking the insert button, a page postback is triggered,The itemserting event of the detailsview control occurs, saving the file to the server file system;Next, the insert () method of the objectdatasource control calls the insertwithpicture method of the categoriesbll class, which in turn calls the insertwithpicture method of the categoriestableadapter.

If the database happens to be offline,Or there is an error in the insert sql statement,What happens then?There is no doubt that adding records will fail.The end result is,Adding records to the database failed.However, the file was successfully uploaded to the server file system.When the insert process throws an exception,The file should be deleted.

In Tutorial 18Handling bll/dal layer exceptions in asp.net pagesWe mentioned that different layers of the system architecture may throw exceptions.At the presentation layer,We can determine whether an exception occurred through the itemserted event of the detailsview control.Also provide the insertparameters parameter value of the objectdatasource control. Therefore, we create an event handler for the itemsetted event,Check if an exception is thrown,If so, delete the file specified by the objectpathsource control's brochurepath parameter.

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

to sum up

We need to go through a few steps to create a web-based add interface,This interface allows adding records containing binary data.If you choose to store directly in the database,We will make some adjustments to the architecture,In order to insert binary data,Need to add corresponding methods;After adjusting the architecture, the next step is to create an add interface.You can use the detailsview control and customize it to include the fileupload control. Uploaded files can be stored in the server's file system,Or assign a data source parameter to the itemserting event handler of the detailsview control.

To save data in the file system, you also need to choose a naming system.This prevents files uploaded by one user from overwriting files uploaded by another user.In addition, when inserting data into the database fails,The uploaded file must be deleted.

Now we can add new categories to the system with their pictures and instruction booklets.In the next chapter we explore how to update existing classes,And how to properly remove the corresponding binary data when deleting a class.

Happy programming!

About the Author

  • Previous Analysis of join usage of CI framework database query
  • Next CI framework AR operation (array form) method for inserting multiple pieces of SQL data