haoxiaobo

从C到C++又到.net, 有一些心得, 和大家交流下...
  博客园  :: 首页  :: 新随笔  :: 联系 :: 订阅 订阅  :: 管理

需求是:
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, 好了. 现在已经可以动态地生成图像流了, 没有临时文件, 一切干干净净