Oracle中BLOB字段存储4GB以下视频数据的方法

it2024-11-19  11

  很长时间没亲自写写东西了,只是收集转载了一些好资料,其实,真正静下心总结一下,可以写的知识点很多。与困难做斗争,挑战技术难关,总会有些感受心得的。

  今天想和网友分享一下“Oracle中BLOB大字段如何读写视频数据”,这个话题起因是我在使用ORACLE备份数据时,误删了数据库实例的控制文件,导致项目数据需要重新入库。也就是我在弥补这个错误时,发现之前的数据入库功能,都没有把200M以上的视频数据导入ORACLE的BLOB字段里,也就是之前的写入BLOB字段数据的方法失效了。这是个惊人的发现,我发现因为这个程序BUG我遗漏掉近300G的视频数据,某些单个视频文件数据量达到3.6G。

  我研究基于ORACLE Text的全文检索功能,开始接触ORACLE的BLOB字段,3年多了,自认为已经熟知BLOB字段的操作。但这次的难题迫使我更深入的认识ORACLE的BLOB字段。

  BLOB字段能以二进制形式存放4G数据,200M的视频数据当然应该没问题,可以出错了?!原来的方法会报“Out of memory”错误,PLSQL Developer工具导入大视频数据,同样会报“Out of memory”错误。3.6G的视频数据又该如何导入?原来写入大字段的方法,导入一般的图片和文档一点问题没有。

         ///   <summary>          ///  写大字段内容         ///  (新方法,2010.2.4)         ///   </summary>          ///   <param name="pDbConn"></param>          ///   <param name="strTable"></param>          ///   <param name="strBlobField"></param>          ///   <param name="strFile"></param>          ///   <param name="strWhereClause"></param>          ///   <returns></returns>          public   bool  WriteBlobField(System.Data.OleDb.OleDbConnection pDbConn,             string  strTable,             string  strBlobField,             string  strFile,             string  strWhereClause)        {             if  (strWhereClause  ==   "" )            {                 return   false ;            }             try             {                 string  strSQL  =   " UPDATE  "   +  strTable  +   "  SET  "   +  strBlobField  +   "  =:blob WHERE  "   +  strWhereClause;                OleDbCommand cmd  =   new  OleDbCommand(strSQL, pDbConn);                 // 无需说明类型                 // cmd.Parameters.Add(new OleDbParameter("blob", SqlDbType.VarBinary));                 //  cmd.Parameters.AddWithValue("blob", SqlDbType.Binary);                 FileInfo fileInfo  =   new  FileInfo(strFile);                FileStream fsBlob  =  fileInfo.OpenRead(); //  new FileStream(strFile, FileMode.Open,FileAccess.Read);                  byte [] dataBlob  =   new   byte [fsBlob.Length];// 问题1所在                 fsBlob.Read(dataBlob,  0 System.Convert.ToInt32(fsBlob.Length )); //问题2所在                 fsBlob.Close();                 // 采用新的方法,AddWithValue();                 cmd.Parameters.AddWithValue( " blob " , dataBlob);                 // cmd.Parameters["blob"].Value = dataBlob;                  int  result  =  cmd.ExecuteNonQuery();                 if  (result  <   1 )                {                     return   false ;                }            }             catch  (Exception ex)            {                 //    MessageBox.Show(ex.Message, "写数据", MessageBoxButtons.OK);                  return   false ;            }             return   true ;        }             ///   <summary>              ///  将字符串写成大字段内容                ///  (2010.2.4 修改)             ///   </summary>              ///   <param name="pDbConn"></param>              ///   <param name="strTable"></param>              ///   <param name="strBlobField"></param>              ///   <param name="strBlobContent"></param>              ///   <param name="strWhereClause"></param>              ///   <returns></returns>              public   bool  WriteBlobField2(System.Data.OleDb.OleDbConnection pDbConn,                           string  strTable,                          string  strBlobField,                          string  strBlobContent,                          string  strWhereClause)            {                 if  (strWhereClause  ==   "" )                {                     return   false ;                }                 try                 {                     string  strSQL  =   " UPDATE  "   +  strTable  +   "  SET  "   +  strBlobField  +   "  =:blob  "   +                          " WHERE  "   +  strWhereClause;                    OleDbCommand cmd  =   new  OleDbCommand(strSQL, pDbConn);                    cmd.Parameters.Add(strBlobField, SqlDbType.Binary);                     //     byte[] dataBlob = new byte[strBlobContent.Length];                      byte [] dataBlob  =  System.Text.Encoding.Default.GetBytes(strBlobContent);                    cmd.Parameters[ " blob " ].Value  =  dataBlob;                     int  result  =  cmd.ExecuteNonQuery();                     if  (result  <   1 )                    {                         return   false ;                    }                }                 catch  (Exception ex)                {                    MessageBox.Show(ex.Message,  " 写数据 " , MessageBoxButtons.OK);                     return   false ;                }                 return   true ;            }

 

 问题1:无法一次性开辟足够大空间(如1G),写入大视频时,会导致报内存不足。

 问题2:System.Convert.ToInt32()会使3G的视频时,会报类型转换失败,数值值过大。

  上面两个问题在网络中所有的方法中都普遍存在的,都会导致无法导入700M以上的视频数据。

  OLEDB方法对ORCLE 8以后的大字段操作不在支持,我在解决问题的过程中转向了OracleClient命名空间下的方法来操作BLOB大字段,主要参考微软官方http://msdn.microsoft.com/zh-cn/library/cydxhzhz(v=VS.90).aspx和博客园中的http://www.cnblogs.com/zhengmaoch/archive/2005/08/10/212014.html。这两份资料对我解决500M以下数据量的视频很有帮助,但是1G甚至是3G以上视频数据是无法解决的。上面两处使用了事务处理在导500M以上数据时,会报“ORA-22297: warning: Open LOBs exist at transaction commit time ”错误,主要因为提交事务时数据文件没有读完。

  经过试验和参考http://msdn.microsoft.com/en-us/library/system.io.filestream.read.aspx方法,终于完全解决上面两个问题,实现大视频量数据导入BLOB字段。

         ///   <summary>          ///  2010.10.22         ///  读取视频数据进入ORACLE大字段中         ///   </summary>          ///   <param name="fileToUpload"></param>          ///   <param name="uploadSQL"></param>          ///   <returns></returns>          public   bool  OracleUpload( string  fileToUpload,  string  uploadSQL)        {             /*              * Get Connected              */              string  connection  =  strConn;            OracleConnection conn;            conn  =   new  OracleConnection(connection);            conn.Open();            OracleCommand cmd  =   new  OracleCommand(uploadSQL, conn);            OracleTransaction transaction  =  conn.BeginTransaction();            cmd.Transaction  =  transaction;            OracleDataReader reader  =  cmd.ExecuteReader();             using  (reader)            {                 try                 {                    reader.Read();                    OracleLob tmpBlob  =  reader.GetOracleLob( 4 );                    reader.Close();                    FileStream fsBlob  =   new  FileStream(fileToUpload, FileMode.OpenOrCreate, FileAccess.Read);                     // BinaryReader br = new BinaryReader(fs);                     tmpBlob.BeginBatch(OracleLobOpenMode.ReadWrite);                     long  length  =  fsBlob.Length;                     int  numBytesToRead  =  System.Convert.ToInt32(length  /   10 ); // 解决问题2                      int  numBytesRead  =   0 ;                     int  n;                     byte [] Buffer  =   new   byte [numBytesToRead];                     // 2010.10.25  修改加 将文件分为10块 防止文件为3.3G以上                      // 解决问题1                      for  ( int  i  =   0 ; i  <   9 ; i ++ )                     {                                                n  =   0 ;                        //  numBytesToRead = length / 5;                         Buffer  =   new   byte [numBytesToRead];                         numBytesRead  =   0 ;                         while  ((n  =  fsBlob.Read(Buffer, numBytesRead, numBytesToRead))  >   0 )                        {                            numBytesRead  +=  n;                            numBytesToRead  -=  n;                        }                        numBytesToRead  =  System.Convert.ToInt32(length  /   10 );                        tmpBlob.Write(Buffer,  0 , numBytesToRead);                    }                           numBytesToRead  =  System.Convert.ToInt32(length  /   10 +  length  %   10 );                    numBytesRead  =   0 ;                    n  =   0 ;                     int  tmpLength  =  numBytesToRead;                     byte [] Buffer2  =   new   byte [tmpLength];                     while  ((n  =  fsBlob.Read(Buffer2, numBytesRead, numBytesToRead))  >   0 )                    {                        numBytesRead  +=  n;                        numBytesToRead  -=  n;                    }                     // numBytesToRead = tmpLength;                     tmpBlob.Write(Buffer2,  0 , tmpLength);                    fsBlob.Close();                    tmpBlob.EndBatch();                    cmd.Parameters.Clear();                    Buffer  =   null ;                    }                 catch (Exception ex)                {                    MessageBox.Show( " 出错: " + ex.Message);                     // 关闭                     reader.Close();                    transaction.Commit();                    conn.Close();                     return   false ;                }            }            reader.Close();            transaction.Commit();            conn.Close();             return   true ;        }

 

   上面的方法完全能处理4G以下的视频数据的导入问题,已经经过验证的。PLSQL Developer工具同样无法读取BLOB字段中的大数据量的视频,如需读取请详细参照http://www.cnblogs.com/wuhenke/archive/2010/10/25/1860752.html

         ///   <summary>          ///  从数据库中读出大字段到文件中         ///   </summary>          ///   <param name="uploadSQL"></param>          ///   <returns></returns>          public   bool  OracleRead( string  uploadSQL)        {             string  connection  =  strConn;            OracleConnection conn;            conn  =   new  OracleConnection(connection);            conn.Open();            OracleCommand cmd  =   new  OracleCommand(uploadSQL, conn);             long  readStartByte  =   0 ; // 从BLOB数据体的何处开始读取数据              int  hopeReadSize  =   1024 // 希望每次从BLOB数据体中读取数据的大小              long  realReadSize  =   0 ; // 每次实际从BLOB数据体中读取数据的大小             // CommandBehavior.SequentialAccess将使OracleDataReader以流的方式加载BLOB数据              string  filename  =   " F:\\Test " + DateTime.Now.Day + DateTime.Now.Minute + DateTime.Now.Second + " .avi " ;            OracleDataReader dr  =  cmd.ExecuteReader(CommandBehavior.SequentialAccess);             while  (dr.Read())            {                FileStream fs  =   new  FileStream(filename, FileMode.Create);                 byte [] buffer  =   new   byte [hopeReadSize];                realReadSize  =  dr.GetBytes( 0 , readStartByte, buffer,  0 , hopeReadSize);                 // 循环,每次读取1024byte大小,并将这些字节写入流中                  while  (( int )realReadSize  ==  hopeReadSize)                {                    fs.Write(buffer,  0 , hopeReadSize);                    readStartByte  +=  realReadSize;                    realReadSize  =  dr.GetBytes( 0 , readStartByte, buffer,  0 , hopeReadSize);                }                 // 读取BLOB数据体最后剩余的小于1024byte大小的数据,并将这些字节写入流中                 realReadSize  =  dr.GetBytes( 0 , readStartByte, buffer,  0 , hopeReadSize);                fs.Write(buffer,  0 , ( int )realReadSize);            }                        // transaction.Commit();             conn.Close();                 return   true ;        }

 

 

参考资料:

http://msdn.microsoft.com/zh-cn/library/cydxhzhz(v=VS.90).aspx

http://www.cnblogs.com/zhengmaoch/archive/2005/08/10/212014.html

http://msdn.microsoft.com/en-us/library/dyh7k75y(vs.71).aspx

http://www.pczpg.com/a/2010/0719/14962.html

http://msdn.microsoft.com/en-us/library/system.io.filestream.read.aspx

http://www.csharp-examples.net/filestream-read-file/

http://blog.csdn.net/lonet/archive/2010/03/03/5342386.aspx

 

本博客声明:本人的技术探索过程中,得到了国信司南公司方面支持。今后,本人博客里的所有技术探索成果将归“无痕客”、“国信司南”和“博客园”三方共同所有,原创作品如需转载,请注明本博客声明。

 

转载于:https://www.cnblogs.com/wuhenke/archive/2010/10/25/1860809.html

相关资源:数据结构—成绩单生成器
最新回复(0)