需求是:
1.网站需要提供图片上传
2.图片不能用文件的方式保存, 只能保存到数据里.
3.查询页面里动态生成图像.
一般都是用保存图片文件的方式, 在服务器上找个地方存下来, 然后数据库保存这个文件的WEB链接. 但是如果不允许用保存文件的方式, 而要求必须将图片数据直接存在数据库里呢?
第一个问题是解决上传和保存的问题. 比如有一个网站提供DVD的资料, 用户可以自行加入新的DVD介绍文字和一个说明图片进去. 介绍和图片都放到数据库里, 文字资料不用说了, 主要是图片.
以SQLSERVER为例:
在SQLSERVER里建一个表来保存相关的信息, 如dvdimg,
CREATE TABLE [dbo].[DvdImg] (
DVDID [int] NOT NULL ,
Img [image] NULL ,
filetype [char] (10) NOT NULL
) ;
DVDid是关联到DVD主信息表的DVD唯一识别符, 为了让数据库快一点, 我把数据和图像分开了.
在上传页面上放一个文件上传控件, 就是下面这个东西:
protected System.Web.UI.HtmlControls.HtmlInputFile fEDImg;
名字就是: fEDImg
加一个保存按钮, 写个click事件的处理, 把数据insert到数据库里, 咱们主要说保存图片的这一段.
if (this.fEDImg.PostedFile.ContentLength != 0)
{
// 加入图片
int intDocLen = this.fEDImg.PostedFile.ContentLength;
byte[] Docbuffer = new byte[intDocLen];
Stream objStream;
objStream = this.fEDImg.PostedFile.InputStream;
objStream.Read(Docbuffer,0,intDocLen);
objStream.Close();
SqlConnection conn = Global.GetConn();
SqlCommand cmd = conn.CreateCommand();
conn.Open();
cmd.CommandText = "delete from dvdimg where dvdid = "+dvd.DVDID.ToString();
cmd.ExecuteNonQuery();
int iLastIndex =this.fEDImg.PostedFile.FileName.LastIndexOf(".");
string sExtFileName = this.fEDImg.PostedFile.FileName.Substring(
iLastIndex + 1,
this.fEDImg.PostedFile.FileName.Length - iLastIndex -1
);
cmd.CommandText ="insert into dvdimg (dvdid, img, filetype) values (@dvdid, @img, @filetype)";
cmd.CommandType = CommandType.Text;
cmd.Parameters.Add("@dvdid",SqlDbType.Int);
cmd.Parameters.Add("@img",SqlDbType.Image);
cmd.Parameters.Add("@filetype",SqlDbType.Char);
cmd.Parameters[0].Value =dvd.DVDID;
cmd.Parameters[1].Value = Docbuffer;
cmd.Parameters[2].Value = sExtFileName;
cmd.ExecuteNonQuery();
conn.Close();
}
Global.GetConn()是得到数据库连接对象的方法, 我喜欢把这种全局性的东西放到这里. 不必多说.
这样一来, 图片就保存好了. 注意, 文件格式一定要保存, 不然以后就不知道这是什么东西了.
第二是浏览, 这里有一个问题, 就是图片数据是在数据库里, 而页面上显示图像时用的是<img>元素, 其中的src属性该如何写?
有人这么做: 把图片取出来之后, 生成一个临时文件, 然后把页面文件里的img的src指到这个文件里.
当然这样也可以解决问题, 但是有几个问题: 一是慢, 要写硬盘的, 二是临时文件什么时候删? 定期删吗? 网站访问量大了怎么办? 不好.
最好的解决办法是把img的src指向一个程序, 而不是一个文件, 由这个程序来生成图像, 向浏览器发送.
那么, 生成信息浏览页面时, 在显示画面的地方就可以这样写:
<img src=http://XXX.XXX.XXX/queryimg.aspx?dvdid=123>
向queryimg.aspx传一个参数, 也就是需要的DVD图像的编号.
然后, 可以新建一个queryimg.aspx页面文件, 记得, 一定要把page的Visible设为false, 这样页面的文件就不会出现了, 怎么输出都是靠你的程序.
然后在page_load里写下面一段这样的代码.
private void Page_Load(object sender, System.EventArgs e)
{
// 在此处放置用户代码以初始化页面
this.Visible = false;
int id = Convert.ToInt32( this.Request["id"]);
sqlConnection1 = Global.GetConn();
byte [] arImg;
string sExtFileName;
// 下面这里我用了数据集的绑定。用datareader也可以,这一段无关紧要。
this.sqlDataAdapter1.SelectCommand.Parameters[0].Value = id;
this.sqlDataAdapter1.Fill(this.db1);
if (this.db1.DvdImg.Rows.Count == 0)
{
// 没有的话就转到一个事先准备的“没有图片”的地址上。
this.Response.Redirect("images/noimg.jpg");
return;
}
sExtFileName = this.db1.DvdImg[0].filetype;
arImg = this.db1.DvdImg[0].Img;
// 注意,下面要修改输出的mime格式,不然浏览器是不知道这是什么玩意儿的。
// 不全,也就是个大概齐,大家可以加补充。
string sMimeType;
switch (sExtFileName)
{
case "jpg":
sMimeType = "jpeg";
break;
case "bmp":
sMimeType = "bitmap";
break;
case "gif":
sMimeType = "gif";
break;
case "tif":
sMimeType = "tiff";
break;
default:
sMimeType = "jpeg";
break;
}
Response.ContentType="image/" + sMimeType;
// 写字节流到浏览器。
Response.BinaryWrite(arImg);
this.db1.Dispose();
}
Hmm, 好了. 现在已经可以动态地生成图像流了, 没有临时文件, 一切干干净净