这里想记录的是使用ADO的方式进行记录
这次所做的是将稍大一点的文件存放到SQL Server服务器上,当然这里稍大一点我目前测试的最大也就40M,应该还可以再加大点。。。不过效率确实蛮低的~
首先ADO方式那几步我就不阐述了,网上一大堆,主要的工作是将文件加入记录的那一步,AddNew之后如何做呢? 对于普通的字符串或者常量直接将当前游标所指记录的值进行赋值即可,但是大的文件当然不能直接赋值咯。。。这里关键的函数就是AppendChunck()函数和GetChunck()函数,顾名思义就是对于大块数据进行的操作。而且中对于二进制文件进行传输需要使用到 SAFEARRAY,SAFEARRAYBOUND,具体详见本菜鸟另一篇文章《SAFEARRAY与SAFEARRAYBOUND的相关知识点》。
1 CFile file; 2 CFileException excp; 3 CString FilePathName; 4 5 // 选择文件,当然格式不会有神马限制,这里会把所有文件作为内存中的一个块完全按照二进制形式存放到服务器上 6 CFileDialog dlg(TRUE); 7 if(dlg.DoModal()==IDOK) 8 { 9 FilePathName=dlg.GetPathName(); 10 } 11 12 // 进行数据库操作 13 try 14 { 15 16 if(file.Open(FilePathName, CFile::modeRead | CFile::typeBinary, &excp)) 17 { 18 19 int nSize = file.GetLength(); 20 BYTE * pBuffer = new BYTE [nSize]; //按文件的大小在堆上申请一块内存 21 BYTE* pBufferForDel = pBuffer; 22 if (file.Read(pBuffer, nSize) > 0 ) //把文件读到pBuffer 23 { 24 VARIANT varBLOB; 25 SAFEARRAY *psa; 26 SAFEARRAYBOUND rgsabound[1]; 27 28 m_pRecordset->AddNew(); 29 30 m_pRecordset->PutCollect("id",nID); 31 m_pRecordset->PutCollect("name",strName); 32 33 if(pBuffer) 34 { 35 rgsabound[0].lLbound = 0; 36 rgsabound[0].cElements = nSize; 37 psa = SafeArrayCreate(VT_UI1, 1, rgsabound); 38 // 这里将数据一个字节一个字节进行复制 39 // long 类型应该够了吧。。。 40 for (long i = 0; i < (long)nSize; i++) 41 SafeArrayPutElement (psa, &i, pBuffer++); 42 43 varBLOB.vt = VT_ARRAY | VT_UI1; 44 varBLOB.parray = psa; 45 m_pRecordset->GetFields()->GetItem("FileSource")->AppendChunk(varBLOB); 46 } 47 m_pRecordset->Update(); 48 49 delete [] pBufferForDel; //记得释放~ 50 pBuffer= NULL; 51 } 52 file.Close(); 53 } 54 } 55 catch (CException* e) 56 { 57 // 进行出错处理 58 }
其实将大型文件存放到SQL Server不是很好的方法,最好可以把文件的存放路径保存到服务器上,用的时候将路径取得之后在文件系统中寻找更好~
读取服务器上的文件应该大家也明白了,主要利用GetChunck()方法,然后读到一片缓冲区上,之后将这块存储区的内容写到文件中,当然SQL Server中记录着该文件的的文件名字和格式,因为我们是将文件完整的按字节保存的,所以取出之后按原格式保存会和原来的文件保持一致的~
代码如下:
1 // 将游标移动到自己需要取得的记录位置 2 m_pRecordset->MoveFirst(); 3 m_pRecordset->Move(nSel); 4 // 获取当前参数 5 int nid = m_pRecordset->GetCollect("id"); 6 CString strFileName =(LPCTSTR)(_bstr_t)(m_pRecordset->GetCollect("name")); 7 8 try 9 { 10 // 首先获得文件的大小 11 long lSize = m_pRecordset->GetFields()->GetItem("FileSource")->ActualSize; 12 if (lSize > 0) 13 { 14 _variant_t varBLOB; 15 varBLOB = m_pRecordset->GetFields()->GetItem("FileSource")->GetChunk(lSize); 16 if(varBLOB.vt == (VT_ARRAY | VT_UI1)) 17 { 18 if(BYTE *pBuffer = new BYTE [lSize+1]) ///重新申请必要的存储空间 19 { 20 SafeArrayAccessData(varBLOB.parray,(void **)&pBuffer); 21 memcpy(pBuffer,pBuffer,lSize); //复制数据到缓冲区m_pBMPBuffer 22 SafeArrayUnaccessData (varBLOB.parray); 23 long nSize = strlen(pBuffer); 24 25 string strName(wChar2Char(strFileName.GetBuffer())); 26 strFileName.ReleaseBuffer(); 27 28 string strPath("E:\\Test\\"); 29 strPath += strName; 30 // 31 USES_CONVERSION; 32 LPCTSTR lpPath = A2W(strPath.c_str()); 33 // 34 CFile out(lpPath,CFile::modeCreate | CFile::modeWrite); 35 out.Write(pBuffer,lSize); 36 out.Close(); 37 38 delete [] pBuffer; 39 pBuffer = NULL; 40 } 41 } 42 43 } 44 } 45 catch (_com_error e) 46 { 47 AfxMessageBox(e.ErrorMessage()); 48 }
在使用这个方法之前,我是在向本地的SQL Server服务器上写入大数据的,用的是ODBC的方法,执行SQL语句
" Insert into Table select '1001', 'You are my world.wav', BulkColumn FROM OPENROWSET(Bulk 'E:\\Test\\You are my world.wav' SINGLE_BLOB)"
这里是指定要写入文件的路径,然后执行SQL语句,这里服务器会进行搜索其可见的路径下的该文件,然后将其读入数据库作为记录的一列~ 对于本地很好用,但是如果是要向远程服务器上写入大文件,MSDN文档上说明需要使用UNC命名 "\\HostName\\SharFolder\\File" 而且登陆SQL的账户需要有权限访问该路径,而文件所存放的本地主机需要向服务器开放自己的文件夹。。。不过这种方法,由于我对SQL Server不太熟悉,尤其是2005稍微有点扯的产品,头大!一直没有解决账户权限访问的问题。。。
如果有人清楚的话 也可以跟我说下,万分感谢。。。哈哈