ADO.NET笔记——存储二进制大对象(BLOB)

相关知识

上传二进制大对象(Binary Large Object)(如图片、视频等)的基本编程步骤是:

  • 在数据库中使用varbinary(MAX)、varchar(MAX)或者nvarchar(MAX)等数据类型记录BLOB
  • 使用INSERT INTO语句,在表中建立一个新行
  • 使用 UPDATE xxx.WRITE 语句,逐段将BLOB写入大数据字段中

代码示例

  1. 数据库:沿用AccountDBforSQLInjection,使用下列的SQL语句建立一个包含varbinary(MAX)字段类型的表:

        CREATE TABLE [dbo].[Photo](
            [PhotoID] [int] IDENTITY(1,1) NOT NULL,
            [PhotoName] [nvarchar](50) NULL,
            [PhotoData] [varbinary](max) NULL,
         CONSTRAINT [PK_Photo] PRIMARY KEY CLUSTERED
        (
            [PhotoID] ASC
        )WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY]
        ) ON [PRIMARY] TEXTIMAGE_ON [PRIMARY]

    该表的主键PhotoID是自增长列

  2. SQL语句定义:

    static string strConn = @"server=Joe-PC;database=AccountDBforSQLInjection;uid=sa;pwd=root";
    //以下SQL命令在Photo表中创建一个新行,并且返回新行的PhotoID(该字段是自增长的)
    //PhotoData不能初始化为NULL,因为.WRITE字句无法在NULL字段上操作
    static string strInsertCmd = @"INSERT INTO Photo(PhotoName, PhotoData) Values(@PhotoID, 1);SELECT @[email protected]@IDENTITY";
    //@@identity的作用是返回最后插入的标识值,使用它来获取插入数据后的标识符。
    //但有一点是需要注意的,@@identity返回的是最后的标识符,所以,要想正确的返回插入后的标识符,那么就必须保证,你想要的结果是最后的标识符,否则就会隐藏bug。
    //以下SQL语句用于更新PhotoData字段中的一部分数据
    //@data表示待写入的数据,@offset表示在该字段的哪个位置开始写起,@length表示写入多少个字节
    static string strUploadCmd = @"UPDATE Photo SET PhotoData.WRITE(@data, @offset, @length) WHERE [email protected]";
  3. 创建新行:

            // 在图片表中创建新行
            static int InsertPhoto()
            {
                using (SqlConnection conn = new SqlConnection(strConn))
                {
                    conn.Open();
                    SqlCommand cmd = new SqlCommand(strInsertCmd, conn);
                    cmd.Parameters.Add("@PhotoName", SqlDbType.NVarChar, 50).Value = "flower.jpg";
                    SqlParameter id = cmd.Parameters.Add("@PhotoID", SqlDbType.Int);
                    id.Direction = ParameterDirection.Output;
                    cmd.ExecuteNonQuery();
                    return Convert.ToInt32(id.Value);
                }
            }
  4. 向新行中写入图片数据:

            //从磁盘文件读入图片数据,然后逐段写入数据库
            static void UploadPhoto(int photoID)
            {
                using (SqlConnection conn = new SqlConnection(strConn))
                {
                    SqlCommand cmd = new SqlCommand(strUploadCmd, conn);
                    cmd.Parameters.Add("@PhotoID", SqlDbType.Int).Value = photoID;
                    SqlParameter data = cmd.Parameters.Add("@data", SqlDbType.VarBinary, -1);
                    SqlParameter offset = cmd.Parameters.Add("@offset", SqlDbType.Int);
                    offset.Value = 0;
                    SqlParameter length = cmd.Parameters.Add("@length", SqlDbType.Int);
    
                    int BUF_SIZE = 1024;
                    string fileName = "..\\..\\picture.jpg";
    
                    FileStream fs = new FileStream(fileName, FileMode.Open, FileAccess.Read);
                    byte[] buf = new byte[BUF_SIZE];
                    int read = fs.Read(buf, 0, BUF_SIZE);
                    conn.Open();
    
                    while (read > 0)
                    {
                        length.Value = read;
                        data.Value = buf;
                        cmd.ExecuteNonQuery();
                        offset.Value = Convert.ToInt32(offset.Value) + read;//偏移量逐次递进
                        read = fs.Read(buf, 0, BUF_SIZE);
                    }
    
                    fs.Close();
                }
            }
  5. 下载图片数据,检测上传是否成功:

            //将前述写入的图片从数据库读出来,保存到文件中
            static void DownloadPhoto(int photoID)
            {
                using (SqlConnection conn = new SqlConnection(strConn))
                {
                    string strCmd = "select * From photo where PhotoID=" + photoID.ToString();
                    SqlCommand cmd = new SqlCommand(strCmd, conn);
                    conn.Open();
                    SqlDataReader dr = cmd.ExecuteReader(CommandBehavior.SequentialAccess);
                    dr.Read();
                    FileStream fs = new FileStream("..\\..\\savedpicture.jpg", FileMode.OpenOrCreate, FileAccess.Write);
                    byte[] buf = new byte[1024];
                    long bytesRead = 0;
                    long startIndex = 0;
                    while ((bytesRead = dr.GetBytes(2, startIndex, buf, 0, 1024)) > 0)
                    {
                        fs.Write(buf, 0, (int)bytesRead);
                        startIndex += bytesRead;
                    }
                    fs.Close();
                }
            }
  6. 主函数:

            static void Main(string[] args)
            {
                int photoID = InsertPhoto();
                UploadPhoto(photoID);
                DownloadPhoto(photoID);
            }

程序说明

本程序将从项目文件夹上传picture.jpg文件至数据库,然后又从数据库中下载数据保存为savedpicture.jpg文件,以便检查上传是否成功

时间: 2024-10-07 06:09:41

ADO.NET笔记——存储二进制大对象(BLOB)的相关文章

oracle学习笔记 存储及raid技术概述

oracle学习笔记 存储及raid技术概述 本课以oracle数据库所运行的环境,讲一下存储和raid技术. 一)oralce生产环境里的结构 先说一下oracle所在的环境. 有一种结构: 两个服务器.两个光纤存储交换机.两个存储, 它们通过网线相互连接在一起. 每个服务器接两个交换机, 每个交换机连两个服务器同时连两个存储, 每个存储和两个交换机有连接. 这是oracle数据库相对比较典型的正规的运行环境. 每个服务器上都装Linux和oracle数据库软件, oracle数据库建在存储上

3.2 配置构建Angular应用——简单的笔记存储应用

本节我们会通过构建一个简单的笔记存储应用(可以载入并修改一组简单的笔记)来学习如何应用Angular的特性.这个应用用到的特性有: 在JSON文件中存储笔记 展示.创建.修改和删除笔记 在笔记中使用Markdown格式 同步编辑和预览Markdown 本应用已经包含了基础的HTML和CSS代码,还有一个用Node写的简单的RESTful服务器,用于管理笔记,这样我们就可以专注于Angular而不是API.我们学习的重点是如何把Angular加入其中并学习它的重要特性. 3.2.1 获取项目文件

memcached学习笔记——存储命令源码分析下篇

上一篇回顾:<memcached学习笔记——存储命令源码分析上篇>通过分析memcached的存储命令源码的过程,了解了memcached如何解析文本命令和mencached的内存管理机制. 本文是延续上一篇,继续分析存储命令的源码.接上一篇内存分配成功后,本文主要讲解:1.memcached存储方式:2.add和set命令的区别. memcached存储方式 哈希表(HashTable) 哈希表在实践中使用的非常广泛,例如编译器通常会维护的一个符号表来保存标记,很多高级语言中也显式的支持哈希

memcached学习笔记——存储命令源码分析上

原创文章,转载请标明,谢谢. 上一篇分析过memcached的连接模型,了解memcached是如何高效处理客户端连接,这一篇分析memcached源码中的process_update_command函数,探究memcached客户端的set命令,解读memcached是如何解析客户端文本命令,剖析memcached的内存管理,LRU算法是如何工作等等. 解析客户端文本命令 客户端向memcached server发出set操作,memcached server读取客户端的命令,客户端的连接状态

mysql笔记(存储引擎)

读写锁:. 表级锁:开销小,加锁快:不会出现死锁:锁定粒度大,发生锁冲突的概率最高,并发度最低. 行级锁:开销大,加锁慢:会出现死锁:锁定粒度最小,发生锁冲突的概率最低,并发度也最高. 页面锁:开销和加锁时间界于表锁和行锁之间:会出现死锁:锁定粒度界于表锁和行锁之间,并发度一般 对MyISAM表的读操作,不会阻塞其他用户对同一表的读请求,但会阻塞对同一表的写请求:对MyISAM表的写操作,则会阻塞其他用户对同一表的读和写请求:MyISAM表的读和写操作之间,以及写和写操作之间是串行的!(当一线程

ADO.NET笔记——使用连接池

相关知识: 连接池的意义: 应用程序往往涉及大量的,并发的数据访问操作 数据库服务器能够同时维系的连接数量非常有限.如果某个数据库访问操作不及时关闭连接,就会减少其他调用对数据库访问的机会.因此,一般需要尽可能晚的打开连接,尽可能早的关闭连接 反复的创建和销毁连接对象,或者反复的打开和关闭实际的连接(从应用程序到数据库服务器,可能跨网络),其开销是比较大的,也是不划算的 采用连接池,在池中缓存若干个链接对象.如果有调用需要使用连接,则从池中取出一个:调用完成后,并不销毁连接,而是将连接放回池中,

ADO.NET笔记——使用DataAdapter执行增删改操作

相关知识: DataSet中的数据一旦从数据库下载下来,便不再与数据库保持联系.如果修改了DataSet中的数据,需要重新建立连接,并且通过SQL命令将修改更新到数据库去 编写SQL命令往往比较繁琐和机械化,ADO.NET提供了一个SqlCommandBuilder对象,帮助DataAdapter对象从SELECT语句推算出需要的UPDATE,DELETE和INSERT语句:然后DataAdapter便可以利用这些语句,检查DataSet中被修改的数据,然后提交到数据库 SqlCommandBu

ADO.NET笔记——调用存储过程

相关知识: 在ADO.NET访问SQL Server时,鼓励使用存储过程取代常规的SQL语句. 存储过程有下列优点: 存储过程中的SQL语句将会经过预先的解析和编译,然后存放在数据库服务器上行.调用的时候不必在此解析语法和编译,因此效率比采用常规SQL语句高 带参数的存储过程在一定程度上可以降低SQL注入攻击的风险 存储过程便于在数据库服务器上统一管理,减少了程序员维护SQL代码的工作量 存储过程有利于重用某些数据库的访问逻辑 代码示例: 在数据库中创建存储过程(沿用SQLInjection案例

Android笔记:存储相关,getExternalCacheDir, getExternalFilesDir,getExternalStorageDirectory等

File cacheDir = mContext.getExternalCacheDir(); if(null != cacheDir){    mCacheDirPath = cacheDir.getAbsolutePath() + "/images/"; } if(TextUtils.isEmpty(mCacheDirPath)){    if(Environment.MEDIA_MOUNTED.equals(Environment.getExternalStorageState(