前端碰到對在一個系統遇到流程控制中需要存儲在資料庫存儲一個簽名圖檔的問題-一直控制不好, 今天特别關于這個問題詳細看了一下.其實這個問題網上資源還是相當多的,但問題是過于淩亂 資料殘缺不全 甚至我感覺其中有相當的一部分會對讀者産生一些誤導.對于Asp.net中存儲圖檔我在08年一月份就做了一個詳細解決方案,今天在這個基礎主要對一些細節控制上以及頁面顯示上做了完善,詳細步驟如下:
首先聲明一下開發環境:VS2008+SQL2005資料庫+.NET FrameWork 3.5版本
(1)存儲圖檔ImageStore表資料庫設計:
1 create table StoreImage
2 (
3 id int not null identity(1,1) primary key,
4 markname varchar(100) not null,--圖檔備注名稱
5 markContent image not null,--檔案内容
6 markType varchar(100) not null,--儲存檔案類型 用于生成
7 markSize int not null,--圖檔長度 讀取資料用
8 markLinkUrl varchar(1000) not null,--資料庫路徑
9 markDate datetime not null default( getdate())--上傳時間
10 )
11 go
其中在表中設計中添加了上傳圖檔檔案類型和檔案大小(Byte[]位元組大小),主要為了讀取時對圖檔顯示進行控制.請參考後面編碼說明.存儲圖檔内容采用Image類型,SQL2005資料容量為2G,對應C#中類型Byte[](位元組數組),其中在設計中我還參考使用SQL中Binary類型,但是測試後發現Binary類型容量範圍1-8000位元組,對于圖檔容量太小, markLinkUrl為了測試以圖檔路徑方式存儲并讀取顯示在頁面這種方式 請參考後面詳細說明.
(2)圖檔存儲到資料庫并單一讀取:
圖檔存儲:通過檔案上傳擷取圖檔并轉換成Byte[]位元組數組,儲存到資料庫Image字段,頁面設計如下:
1 <!--說面這是全部的頁面代碼:-->
2 <form id="form1" runat="server" style="font-size:12px;" enctype="multipart/form-data">
3
4 備 注:<asp:TextBox ID="markname" runat="server"></asp:TextBox>
5 上 傳:<asp:FileUpload ID="FileUpload1" runat="server" />
6 <asp:Button ID="Button1" runat="server" OnClientClick="return checkClint()" Text="上 傳" onclick="Button1_Click" />
7
8 <script language="javascript" type="text/javascript">
9 function checkClint()
10 {
11 var getmarkname=document.getElementById("markname");
12 var getfile=document.getElementById("FileUpload1");
13
14 if(getmarkname.value=="")
15 {
16 alert('請輸入圖檔備注名稱!');
17 getmarkname.focus();
18 return false;
19 }else if(getfile.value=="")
20 {
21 alert('請選擇上傳檔案路徑!');
22 getfile.focus();
23 return false;
24 }else
25 {
26 return true;
27 }
28 }
29 </script>
30 &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;
31 <asp:Button ID="Button2" runat="server" onclick="Button2_Click" Text="讀取圖檔" />
32
33 &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;
34 <asp:Button ID="Button3" runat="server" onclick="Button3_Click" Text="讀取到Image控件中" />
35 &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;
36 <asp:Button ID="Button5" runat="server" onclick="Button5_Click" Text="存儲連結方式擷取圖檔" />
37 &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;
38 <asp:Button ID="Button4" runat="server" onclick="Button4_Click" Text="下載下傳圖檔" />
39 <br />
40 <br />
41 <asp:Image ID="Image1" runat="server" />
42 <br />
43 圖檔路徑方式讀取:<br />
44 伺服器端:<asp:Image ID="Image2" runat="server" />
45 <br />
46 <asp:Label ID="Label2" runat="server"></asp:Label>
47 <br />
48 用戶端Img:<img alt="" runat="server" id="clintimg" />
49 <br />
50 <asp:Label ID="Label1" runat="server"></asp:Label>
51 </form>
在頁面From表單添加了一個屬性-在頁面Form中設定屬性enctype -設定或擷取表單的 MIME 編碼
單一儲存檔案到資料庫通用方法【注明:通用方法寫在Button1_Click事件中-命名沒有修改主要為了功能】 通用方法如下:
1 /// <summary>
2 /// 上傳檔案同時并儲存到資料中統一
3 /// Author:chenkai Date:2010年2月2日16:24:29
4 /// </summary>
5 protected void Button1_Click(object sender, EventArgs e)
6 {
7 //擷取資料
8 string getname = this.markname.Text;
9 string getfile = this.FileUpload1.PostedFile.FileName;
10
11 //上傳檔案
12 string getlastpath = FileUploadCompant(this.FileUpload1);
13
14 //擷取上傳檔案流
15 byte[] getbyte=new byte[this.FileUpload1.PostedFile.ContentLength];
16 Stream filestream = this.FileUpload1.PostedFile.InputStream;
17
18 //讀入資料
19 filestream.Read(getbyte, 0, this.FileUpload1.PostedFile.ContentLength);
20
21 //插入資料
22 #region
23 string sql = "insert into StoreImage(markname,markContent,markType,markSize,markLinkUrl) values(@name,@content,@type,@size,@link)";
24
25 SqlParameter[] getpars = new SqlParameter[5];
26 getpars[0] = new SqlParameter("@name", getname);
27 getpars[1] = new SqlParameter("@content", getbyte);//檔案内容插入This.Fileupload1.FileBytes同樣可以直接轉換成Byte數組不用轉換
28 getpars[2] = new SqlParameter("@type", this.FileUpload1.PostedFile.ContentType);//儲存檔案類型
29 getpars[3] = new SqlParameter("@size", this.FileUpload1.PostedFile.ContentLength);//檔案長度
30 getpars[4] = new SqlParameter("@link", getlastpath);
31
32 int getrescount = DBUtility.SqlHelper.ExecuteNonQuery(DBUtility.SqlHelper.connString,CommandType.Text,sql,getpars);
33
34 if (getrescount == 1)
35 {
36 //添加成功
37 ScriptManager.RegisterStartupScript(this.Page, this.GetType(), "aler ", "alert( '圖檔記錄成功添加到資料庫'); ", true);
38 }
39 else
40 {
41 //添加失敗
42 ScriptManager.RegisterStartupScript(this.Page, this.GetType(), "aler ", "alert( '圖檔記錄添加失敗'); ", true);
43 }
44
45 #endregion
46 }
這種獲得圖檔轉換成Byte【】位元組數組通用,注意:其中在Fileupload控件中有個GetBytes屬性:This.Fileupload1.FileBytes可以直接把上傳内容轉換成位元組數組而不必通過檔案流來讀取上傳檔案内容,這種方式更為快捷;如上圖檔成功儲存到資料庫 接下來就是如何讀取和顯示控制的問題:
(3)資料庫存儲圖檔的讀取和顯示控制:
從資料庫中讀取到位元組流後把圖檔直接寫入頁面并對顯示進行控制 讀取方法如下【該方法下載下傳Button2_Click中】:
2 /// 讀取資料庫中圖檔并顯示出來
3 /// Author:chenkai Date:2010年2月2日16:48:18
5 protected void Button2_Click(object sender, EventArgs e)
7 //獲得資料
8 string sql = "select * from StoreImage order by id desc";
9
10 #region
11 using (SqlDataReader getreader = DBUtility.SqlHelper.ExecuteReader(DBUtility.SqlHelper.connString, CommandType.Text, sql))
12 {
13 if (getreader != null&&getreader.HasRows)
14 {
15 //讀取資料
16 while (getreader.Read())
17 {
18 Response.ContentType = getreader["markType"] as string;
19 Response.OutputStream.Write(getreader["markContent"] as byte[], 0, Convert.ToInt32(getreader["markSize"].ToString()));
20 Response.End();
21 }
22 }
23 }
24 #endregion
25 }
資料庫中MarkType字段用來設定請求回發頁面内容類型,獲得位元組流後把回發内容轉換成輸出流然後輸出到目前頁面,當然也可以直接使用Response.BinaryWrite()寫入頁面,圖檔自動顯示.但是有人會說我想把它顯示在頁面一個Image控件中或是放到一個DIV層中這樣實際需求. 這就是在實際需求對讀取圖檔進行顯示控制問題. 制作過驗證碼圖檔應該知道,驗證碼生成圖檔單獨放在一個頁面讓後通過Image控件的ImageUrl來連結該頁面,即可實作在Image控件控制,同理這種方式也是适用的: -添加了一個頁面用來存儲生成圖檔,詳細代碼如下:OutPutImageDemo頁面背景編碼:
1 //PageLoad事件中加載圖檔 并顯示到OutPutImageDemo頁面
2 //Author:陳凱 Date:2010年2月3日10:28:17
3 protected void Page_Load(object sender, EventArgs e)
4 {
5 if (!IsPostBack)
6 {
7 //獲得資料
8 string sql = "select * from StoreImage order by id desc";
10 #region
11 using (SqlDataReader getreader = DBUtility.SqlHelper.ExecuteReader(DBUtility.SqlHelper.connString, CommandType.Text, sql))
12 {
13 if (getreader != null && getreader.HasRows)
14 {
15 //讀取資料
16 while (getreader.Read())
17 {
18 Response.ContentType = getreader["markType"] as string;
19 Response.OutputStream.Write(getreader["markContent"] as byte[], 0, Convert.ToInt32(getreader["markSize"].ToString()));
20 Response.End();
21 }
22 }
23 }
24 #endregion
25 }
26 }
那麼在TestImageStoreToDB.aspx頁面中一個控件中擷取該圖檔 隻需設定圖檔的連結路徑即可 代碼如下:
this.Image1.ImageUrl = "OutPutImageDemo.aspx";//連結輸出圖檔頁面即可
(4)資料庫存儲圖檔路徑方式:
直接在資料庫存儲圖檔對資源圖檔較多, 圖檔檔案較大 類似銀行内部系統中對VIP使用者簽名就是用簽名圖檔方式存儲的 這主要為了安全上考慮,實際需求中使用次數頻繁 且常常更新 無疑中這種使資料庫中資料顯得有些臃腫 資料容量增大,同時也增加了通路資料庫伺服器的負載,而使用存儲圖檔路徑的方式:圖檔檔案放在伺服器硬碟上,資料庫中隻需一個檔案路徑指向硬碟上圖檔檔案即可 存儲方式相比存儲位元組流要簡單容易控制 但是我在網上發現關于這種頁面顯示的諸多問題如下我說明一下再Asp.net對圖檔頁面顯示控制:
資料庫中存儲路徑為絕對路徑: 截圖如下
<a target="_blank" href="http://blog.51cto.com/attachment/201201/122014167.gif"></a>
現在頁面放一個伺服器端的Image控件和Html中的<img/>來驗證. 如果直接用絕對路徑對控件進行指派 頁面并不顯示.其實這就涉及到.net中絕對路徑,相對路徑和虛拟路徑三者之間轉換的問題:
絕對路徑是不行的,在硬碟上存儲檔案命名是唯一的同時不會存在不同的檔案格式相同的命名情況,過濾絕對路徑,從絕對路徑就能看出圖檔的存儲位置就在根目錄下FileuploadDict檔案夾下,我們隻需把這個絕對路徑轉換成程式可用相對路徑即可: 同時我們用伺服器端的Image控件和Html測試一下結果:
1 //getfilepath是獲得資料中圖檔存儲路徑(markLinkUrl字段)資料
2 if (!string.IsNullOrEmpty(getfilepath))
3 {
4 //截取目前圖檔檔案名
5 string getclintpath = getfilepath.Substring(getfilepath.LastIndexOf('\\'));
6
7 // 路徑~/ 僅對 ASP.NET 的伺服器控件起作用 ~/ 的意思是相對站點的虛拟根路徑
8 //“~”表示的路徑是目前應用程式的跟目錄。“~”和上面介紹的“/”最大的差別是由伺服器進行動态解釋
9 getclintpath=@"~\FileuploadDict"+getclintpath;//拼接成用戶端伺服器端路徑
11 //設定Html中<Img/> 注意"~"對基于背景程式來動态解析的 是以Img标簽 添加Runat=“Server” 在伺服器端可以通路到
12 this.clintimg.Src = getclintpath;//測試結果成功顯示
13 }
來看一下對于伺服器端控件頁面控制顯示方式:
1 //getfilepath是從資料庫獲得圖檔存儲絕對路徑[markLinkUrl]的值
2 if (!string.IsNullOrEmpty(getfilepath))
4 //截取檔案的名稱
7 //伺服器端-重置擷取目前路徑下伺服器端虛拟應用程式根路徑-成功
8 //使用"/"所有的路徑都是從站點的跟目錄開始的,例如/default.aspx指向的是localhost/default.aspx
9 this.Image2.ImageUrl = HttpContext.Current.Request.ApplicationPath + @"FileuploadDict" + getfilepath.Substring(getfilepath.LastIndexOf('\\'));
10 //測試結果:頁面成功顯示
11 }
在設定伺服器端Image控件時用的是HttpContent.current.Request[擷取目前請求的HttpRequest對象].ApplicationPath[擷取伺服器上 ASP.NET 應用程式的虛拟應用程式根路徑] 來設定.我們來比對一下頁面的路徑:
Html中<Img/>标簽: ~\FileuploadDict\2010-02-03-08-35-47rr4hnz45msimfqzh4tcdv545http_imgload6.jpg
Image服務端控件: /FileuploadDict\2010-02-03-08-35-47rr4hnz45msimfqzh4tcdv545http_imgload6.jpg
上面用的都是虛拟目錄下相對路徑來通路,如果直接通過拼接類似如上字元串 來對控件指派 在Html頁面時不識别的 ~/ 僅對 ASP.NET 的伺服器空件起作用,.其實這就是關于Asp.net中相對路徑的使用問題:處理方式如下;
(A): 如果連結中,源端點和目标端點在同一個目錄下,則在連結中隻需要指明目标端點的文檔名稱就可以了
(B):使用"/"所有的路徑都是從站點的跟目錄開始的,例如/default.aspx指向的是localhost/default.aspx
(C):如果在連結中,源端點和目标端點不位于同一個目錄下,則隻需要将目錄的相對關系表達出來就可以了。如果連結指向的文檔沒有位于目前目錄的子級目錄中,則可以利用”..”符号來表示目前的父目錄,多個..符号可以表示根高的父級目錄,進而建構出目錄的相對位置.
(D):在ASP.NET裡增加了一個新的表達方法“~”,“~”表示的路徑是目前應用程式的跟目錄。“~”和上面介紹的“/”最大的差別是由伺服器進行動态解釋。由于”~”是相對于應用程式的根目錄,是以利用它可以簡化路徑的設定,在某些情況下似乎還必須使用該控件
前面3種方法都是用戶端解析的,也就是html種的源碼就是這樣,由浏覽器來解析定向;而第四種方法是在伺服器端解析,浏覽器并不識别,是以常常在背景拼接時會出現字元串和伺服器控件字元串相同 頁面卻無法顯示圖檔問題,歸咎還是Asp.net相對路徑使用問題.
項目源碼下載下傳位址見附件
本文轉自chenkaiunion 51CTO部落格,原文連結:http://blog.51cto.com/chenkai/765154