前端碰到對在一個系統(tǒng)遇到流程控制中需要存儲在數(shù)據(jù)庫存儲一個簽名圖片的問題-一直控制不好, 今天特別關(guān)于這個問題詳細(xì)看了一下.其實這個問題網(wǎng)上資源還是相當(dāng)多的,但問題是過于凌亂 資料殘缺不全 甚至我感覺其中有相當(dāng)?shù)囊徊糠謺ψx者產(chǎn)生一些誤導(dǎo).對于Asp.net中存儲圖片我在08年一月份就做了一個詳細(xì)解決方案,今天在這個基礎(chǔ)主要對一些細(xì)節(jié)控制上以及頁面顯示上做了完善,詳細(xì)步驟如下:
首先聲明一下開發(fā)環(huán)境:VS2008+SQL2005數(shù)據(jù)庫+.NET FrameWork 3.5版本
(1)存儲圖片ImageStore表數(shù)據(jù)庫設(shè)計:
其中在表中設(shè)計中添加了上傳圖片文件類型和文件大小(Byte[]字節(jié)大小),主要為了讀取時對圖片顯示進行控制.請參考后面編碼說明.存儲圖片內(nèi)容采用Image類型,SQL2005數(shù)據(jù)容量為2G,對應(yīng)C#中類型Byte[](字節(jié)數(shù)組),其中在設(shè)計中我還參考使用SQL中Binary類型,但是測試后發(fā)現(xiàn)Binary類型容量范圍1-8000字節(jié),對于圖片容量太小, markLinkUrl為了測試以圖片路徑方式存儲并讀取顯示在頁面這種方式 請參考后面詳細(xì)說明.
(2)圖片存儲到數(shù)據(jù)庫并單一讀取:
圖片存儲:通過文件上傳獲取圖片并轉(zhuǎn)換成Byte[]字節(jié)數(shù)組,保存到數(shù)據(jù)庫Image字段,頁面設(shè)計如下:
在頁面From表單添加了一個屬性-在頁面Form中設(shè)置屬性enctype -設(shè)置或獲取表單的 MIME 編碼 單一保存文件到數(shù)據(jù)庫通用方法【注明:通用方法寫在Button1_Click事件中-命名沒有修改主要為了功能】 通用方法如下: 這種獲得圖片轉(zhuǎn)換成Byte【】字節(jié)數(shù)組通用,注意:其中在Fileupload控件中有個GetBytes屬性:This.Fileupload1.FileBytes可以直接把上傳內(nèi)容轉(zhuǎn)換成字節(jié)數(shù)組而不必通過文件流來讀取上傳文件內(nèi)容,這種方式更為快捷;如上圖片成功保存到數(shù)據(jù)庫 接下來就是如何讀取和顯示控制的問題: (3)數(shù)據(jù)庫存儲圖片的讀取和顯示控制: 從數(shù)據(jù)庫中讀取到字節(jié)流后把圖片直接寫入頁面并對顯示進行控制 讀取方法如下【該方法下載Button2_Click中】:
數(shù)據(jù)庫中MarkType字段用來設(shè)置請求回發(fā)頁面內(nèi)容類型,獲得字節(jié)流后把回發(fā)內(nèi)容轉(zhuǎn)換成輸出流然后輸出到當(dāng)前頁面,當(dāng)然也可以直接使用Response.BinaryWrite()寫入頁面,圖片自動顯示.但是有人會說我想把它顯示在頁面一個Image控件中或是放到一個DIV層中這樣實際需求. 這就是在實際需求對讀取圖片進行顯示控制問題. 制作過驗證碼圖片應(yīng)該知道,驗證碼生成圖片單獨放在一個頁面讓后通過Image控件的ImageUrl來鏈接該頁面,即可實現(xiàn)在Image控件控制,同理這種方式也是適用的: -添加了一個頁面用來存儲生成圖片,詳細(xì)代碼如下:OutPutImageDemo頁面后臺編碼: 那么在TestImageStoreToDB.aspx頁面中一個控件中獲取該圖片 只需設(shè)置圖片的鏈接路徑即可 代碼如下: (4)數(shù)據(jù)庫存儲圖片路徑方式: 直接在數(shù)據(jù)庫存儲圖片對資源圖片較多, 圖片文件較大 類似銀行內(nèi)部系統(tǒng)中對VIP用戶簽名就是用簽名圖片方式存儲的 這主要為了安全上考慮,實際需求中使用次數(shù)頻繁 且常常更新 無疑中這種使數(shù)據(jù)庫中數(shù)據(jù)顯得有些臃腫 數(shù)據(jù)容量增大,同時也增加了訪問數(shù)據(jù)庫服務(wù)器的負(fù)載,而使用存儲圖片路徑的方式:圖片文件放在服務(wù)器硬盤上,數(shù)據(jù)庫中只需一個文件路徑指向硬盤上圖片文件即可 存儲方式相比存儲字節(jié)流要簡單容易控制 但是我在網(wǎng)上發(fā)現(xiàn)關(guān)于這種頁面顯示的諸多問題如下我說明一下再Asp.net對圖片頁面顯示控制: 數(shù)據(jù)庫中存儲路徑為絕對路徑: 截圖如下 現(xiàn)在頁面放一個服務(wù)器端的Image控件和Html中的<img/>來驗證. 如果直接用絕對路徑對控件進行賦值 頁面并不顯示.其實這就涉及到.net中絕對路徑,相對路徑和虛擬路徑三者之間轉(zhuǎn)換的問題: 絕對路徑是不行的,在硬盤上存儲文件命名是唯一的同時不會存在不同的文件格式相同的命名情況,過濾絕對路徑,從絕對路徑就能看出圖片的存儲位置就在根目錄下FileuploadDict文件夾下,我們只需把這個絕對路徑轉(zhuǎn)換成程序可用相對路徑即可: 同時我們用服務(wù)器端的Image控件和Html測試一下結(jié)果:
來看一下對于服務(wù)器端控件頁面控制顯示方式: 在設(shè)置服務(wù)器端Image控件時用的是HttpContent.current.Request[獲取當(dāng)前請求的HttpRequest對象].ApplicationPath[獲取服務(wù)器上 ASP.NET 應(yīng)用程序的虛擬應(yīng)用程序根路徑] 來設(shè)置.我們來比對一下頁面的路徑: Html中<Img/>標(biāo)簽: ~\FileuploadDict\2010-02-03-08-35-47rr4hnz45msimfqzh4tcdv545http_imgload6.jpg Image服務(wù)端控件: /FileuploadDict\2010-02-03-08-35-47rr4hnz45msimfqzh4tcdv545http_imgload6.jpg 上面用的都是虛擬目錄下相對路徑來訪問,如果直接通過拼接類似如上字符串 來對控件賦值 在Html頁面時不識別的 ~/ 僅對 ASP.NET 的服務(wù)器空件起作用,.其實這就是關(guān)于Asp.net中相對路徑的使用問題:處理方式如下; (A): 如果鏈接中,源端點和目標(biāo)端點在同一個目錄下,則在鏈接中只需要指明目標(biāo)端點的文檔名稱就可以了 (B):使用"/"所有的路徑都是從站點的跟目錄開始的,例如/default.aspx指向的是localhost/default.aspx (C):如果在鏈接中,源端點和目標(biāo)端點不位于同一個目錄下,則只需要將目錄的相對關(guān)系表達出來就可以了。如果鏈接指向的文檔沒有位于當(dāng)前目錄的子級目錄中,則可以利用”..”符號來表示當(dāng)前的父目錄,多個..符號可以表示根高的父級目錄,從而構(gòu)建出目錄的相對位置. (D):在ASP.NET里增加了一個新的表達方法“~”,“~”表示的路徑是當(dāng)前應(yīng)用程序的跟目錄?!啊焙蜕厦娼榻B的“/”最大的區(qū)別是由服務(wù)器進行動態(tài)解釋。由于”~”是相對于應(yīng)用程序的根目錄,所以利用它可以簡化路徑的設(shè)置,在某些情況下似乎還必須使用該控件 前面3種方法都是客戶端解析的,也就是html種的源碼就是這樣,由瀏覽器來解析定向;而第四種方法是在服務(wù)器端解析,瀏覽器并不識別,所以常常在后臺拼接時會出現(xiàn)字符串和服務(wù)器控件字符串相同 頁面卻無法顯示圖片問題,歸咎還是Asp.net相對路徑使用問題.
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
31 <asp:Button ID="Button2" runat="server" onclick="Button2_Click" Text="讀取圖片" />
32
33
34 <asp:Button ID="Button3" runat="server" onclick="Button3_Click" Text="讀取到Image控件中" />
35
36 <asp:Button ID="Button5" runat="server" onclick="Button5_Click" Text="存儲鏈接方式獲取圖片" />
37
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 服務(wù)器端:<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>
2 /// 上傳文件同時并保存到數(shù)據(jù)中統(tǒng)一
3 /// Author:chenkai Date:2010年2月2日16:24:29
4 /// </summary>
5 protected void Button1_Click(object sender, EventArgs e)
6 {
7 //獲取數(shù)據(jù)
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 //讀入數(shù)據(jù)
19 filestream.Read(getbyte, 0, this.FileUpload1.PostedFile.ContentLength);
20
21 //插入數(shù)據(jù)
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);//文件內(nèi)容插入This.Fileupload1.FileBytes同樣可以直接轉(zhuǎn)換成Byte數(shù)組不用轉(zhuǎn)換
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( '圖片記錄成功添加到數(shù)據(jù)庫'); ", true);
38 }
39 else
40 {
41 //添加失敗
42 ScriptManager.RegisterStartupScript(this.Page, this.GetType(), "aler ", "alert( '圖片記錄添加失敗'); ", true);
43 }
44
45 #endregion
46 }
2 /// 讀取數(shù)據(jù)庫中圖片并顯示出來
3 /// Author:chenkai Date:2010年2月2日16:48:18
4 /// </summary>
5 protected void Button2_Click(object sender, EventArgs e)
6 {
7 //獲得數(shù)據(jù)
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 //讀取數(shù)據(jù)
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 }
2 //Author:陳凱 Date:2010年2月3日10:28:17
3 protected void Page_Load(object sender, EventArgs e)
4 {
5 if (!IsPostBack)
6 {
7 //獲得數(shù)據(jù)
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 //讀取數(shù)據(jù)
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 }
2 if (!string.IsNullOrEmpty(getfilepath))
3 {
4 //截取當(dāng)前圖片文件名
5 string getclintpath = getfilepath.Substring(getfilepath.LastIndexOf('\\'));
6
7 // 路徑~/ 僅對 ASP.NET 的服務(wù)器控件起作用 ~/ 的意思是相對站點的虛擬根路徑
8 //“~”表示的路徑是當(dāng)前應(yīng)用程序的跟目錄?!啊焙蜕厦娼榻B的“/”最大的區(qū)別是由服務(wù)器進行動態(tài)解釋
9 getclintpath=@"~\FileuploadDict"+getclintpath;//拼接成客戶端服務(wù)器端路徑
10
11 //設(shè)置Html中<Img/> 注意"~"對基于后臺程序來動態(tài)解析的 所以Img標(biāo)簽 添加Runat=“Server” 在服務(wù)器端可以訪問到
12 this.clintimg.Src = getclintpath;//測試結(jié)果成功顯示
13 }
2 if (!string.IsNullOrEmpty(getfilepath))
3 {
4 //截取文件的名稱
5 string getclintpath = getfilepath.Substring(getfilepath.LastIndexOf('\\'));
6
7 //服務(wù)器端-重置獲取當(dāng)前路徑下服務(wù)器端虛擬應(yīng)用程序根路徑-成功
8 //使用"/"所有的路徑都是從站點的跟目錄開始的,例如/default.aspx指向的是localhost/default.aspx
9 this.Image2.ImageUrl = HttpContext.Current.Request.ApplicationPath + @"FileuploadDict" + getfilepath.Substring(getfilepath.LastIndexOf('\\'));
10 //測試結(jié)果:頁面成功顯示
11 }