前几天做了个文件上传下载的例子,就是对Blob对象的上传下载,现把其中遇到的问题以及解决方法在这里再顺一遍。
大家都知道我们的文件是保存在硬盘,也就是外存中的,那我们在上传的时候就要先把文件调入到内存。而内存的容量是有限的,如果我们的文件过于庞大,就会占用太多的内存,所以我们就要把文件分块,然后再把文件一块一块的读入到内存缓冲区中,然后再把缓冲区中的数据读到数据库Blob对象里。那这个Blob对象又是哪来的呢,这就要求我们先向数据库中注册文件的其它信息,包括文件名,文件类型,上传时间等信息,然后同时返回Blob对象名。所以我们需要建一个类来保存文件的信息,以及控制对文件的读写。好,上传大概就是这么一个过程,那下面我们来结合代码再顺一遍。
- 先对一些变量进行初始化 ,这些变量都是文件类fileInfo里的。
buffer = new byte[blockSize];//建立缓冲区数组
inputStream = new FileStream(path , FileMode.Open, FileAccess.Read, FileShare.Read,4* 1024*1024, true);//建立读文件的数据流 fileSize = inputStream.Length;//获取文件大小 blockCount = (int)(fileSize / blockSize)+1;//对文件进行分块
- 对文件其它信息进行注册,并返回Blob对象
private OracleTransaction insert()//注册文件信息 { OracleConnection conn = new OracleConnection(connString); OracleCommand cmd = new OracleCommand("system.form_zhu.file_insert", conn); cmd.CommandType = CommandType.StoredProcedure; cmd.BindByName = true; OracleParameter para_fileName = new OracleParameter("paraName", fileInfo.fileName);//文件名 para_fileName.Direction = ParameterDirection.Input; cmd.Parameters.Add(para_fileName); OracleParameter para_fileType = new OracleParameter("paraType", fileInfo.fileType);//类型 para_fileType.Direction = ParameterDirection.Input; cmd.Parameters.Add(para_fileType); OracleParameter where_string = new OracleParameter("paraString", OracleDbType.Varchar2);//返回where_string where_string.Direction = ParameterDirection.Output; where_string.Size = 200; cmd.Parameters.Add(where_string); conn.Open(); OracleTransaction txn = conn.BeginTransaction();//开启事务
cmd.ExecuteNonQuery();
此处用了事务处理,注册的时候开启事务,直到写完成的的时候结束事务,以遍读写中断的时候可以回滚重新进行读写。
private void blob(OracleTransaction txn)//获取Blob对象 { OracleCommand cmd = new OracleCommand("system.FORM_ZHU.getBlob", txn.Connection); cmd.CommandType = CommandType.StoredProcedure; cmd.BindByName = true; OracleParameter para_tableName = new OracleParameter("tableName", this .tableName );//表名 para_tableName.Direction = ParameterDirection.Input; cmd.Parameters.Add(para_tableName); OracleParameter para_blobName = new OracleParameter("blobName", this.blobName );//Blob对象列名 para_blobName.Direction = ParameterDirection.Input; cmd.Parameters.Add(para_blobName); OracleParameter para_Wblob = new OracleParameter("whereString",whereString );//Blob对象地址 para_Wblob.Direction = ParameterDirection.Input; cmd.Parameters.Add(para_Wblob); OracleParameter blob = new OracleParameter("temp_blob", OracleDbType.Blob);//返回Blob对象 blob.Direction = ParameterDirection.ReturnValue ; blob.Size = 200000; cmd.Parameters.Add(blob); cmd.ExecuteNonQuery(); fileContent =(OracleBlob ) blob.Value;
此处用的连接还是上面事务的连接,用来获取OracleBlob对象,赋值给FileInfo类的FileContent变量。
- 然后主窗体调用FileInfo里的Read方法,此处的读写为异步读写。何为异步读写,就是开辟另一个线程,和主线程同时在运行,互不干扰。比如说子线程在进行读写的时候,主窗体的进度条随着读写的进度在变化。
读写
1 public void ReadFileToBuffer() 2 { 3 inputStream.BeginRead(buffer, 0, blockSize, WriteFileToBlob, null);//以Begin开头的表示为异步方式,将一块文件读到Buffer里,读完了就调用WriteFileToBlob回调函数 4 } 5 public void WriteFileToBlob(IAsyncResult a) 6 { 7 inputStream.EndRead(a);//结束读这一线程 8 if (i < blockCount)//不是最后一块的时候,将Buffer里的数据写到FileContent这个OracleBlob对象里,写完后调用OnEndWrite1回调函数。 9 { 10 fileContent.BeginWrite(buffer, 0, blockSize, OnEndWrite1, null); 11 12 } 13 if (i == blockCount)//最后一块 14 { 15 fileContent.BeginWrite(buffer, 0, (int)fileSize % blockSize, OnEndWrite2, null); 16 } 17 } 18 private void OnEndWrite1(IAsyncResult a) 19 { 20 OnFileBuffer(new EventArgs());//读一块引发的事件,此处不说 21 fileContent.EndWrite(a); 22 i++; 23 ReadFileToBuffer(); 24 } 25 private void OnEndWrite2(IAsyncResult a) 26 { 27 _txn.Commit();//关闭事务流,结束读写 28 OnFileUpdateComplete(new EventArgs()); 29 }
下载就是上传的一个逆过程,上传懂了下载也就会了,此处不再赘述。