10.1、FileStream
1.1、定义一个流对象
Stream fs = newFileStream(@"D:\FINISH.TXT",FileMode.Open,FileAccess.Read,FileShare.Read);
说明:
Stream是父类,FileStream是子类,父类是一个抽象类,子类才是实现类。
FileMode是一个枚举类型,它有很多值:
Open:表示该流对象应该打开现有文件,如果文件不存在,那么就会打开异常的。
CreateNew:表示创建新文件,如果文件存在,那么就会发生异常。
Create:表示创建新文件,如果文件存在,那么就会被改写(清空再写)。
OpenOrCreate:表示如果文件存在就打开,不存在就创建。
FileAccess:控制本流对文件的访问权限:读、写、读写。
FileShare:当前的流在对指定文件进行访问的时候,控制别的流对该文件的访问权限:读、写、读写、没有任何权限,默认情况下,外界是没有权限来访问该文件的,除非指定。
1.2、读取文件
Stream fs = new FileStream(@"D:\FINISH.TXT",FileMode.Open);
int length = fs.Read(byte[] buffer, intoffset, int count);
buffer:这个是字节数组,是用来存放读取的文件的。
offset:这个是buffer中的相对于起始索引0的偏移量,也就是从offset开始存放的。
count:计划从流文件中读取的字节数,count不要去超越buffer的长度。
返回值:该函数返回从文件中实际读取字节数。0<=返回值<= count
注意:
对于Read函数,只要流没有关闭它所指向的文件,那么第一次读取是从第一个字节开始的,第二次读取从第一次读取的最后一个字节的下一个开始,有seek会自动的将流当前指向的文件的位置往后移动,以后的读取依次类推。
读取文件的时候,fs有一个Length属性,表示它所指向的文件的字节大小,即文件的总长度。
1.3、写入文件
Stream fs = new FileStream(@"D:\FINISH.TXT",FileMode. Create);
fs. Write(byte[] buffer, intoffset, int count);
buffer:这个是字节数组,就是将该字节数组中的数据写入到文件中的。
offset:这个是buffer中的相对于起始索引0的偏移量,也就是从offset开始读取buffer的。
count:从buffer中读取的字节数,写入到文件中,count <=buffer.length - offset。
返回值:该函数无返回值。
注意:对于Write函数,如果文件是已存在的,在第一次write之前,会将文件全部清空,然后只要流没有关闭它所指向的文件,那么第一次写入文件是从文件开头开始写入的,第二次写入是从第一次写入的最后一个字节的下一个开始,有seek会自动的将流当前指向的文件的位置往后移动,以后的写入依次类推。
这种写入只是写入到缓冲区中,文件中还没有,要想真的写入文件,还必须依赖下面的函数:
fs.Flush();
这个是将缓冲区的内容写入到基础流中,并清空缓冲区,这样才可以在文件中查看到内容。只要文件没有被关闭,就可以不停的Write和Flush,后面的写入是往文件末尾写。 如果缓冲区很大,可以Write一部分然后Flush一部分,分多次Write和Flush。
1.4、关闭文件
fs. Close()
这个是将缓冲区的内容写入到基础流中,并清空缓冲区,所以即使没有Flush(),调用的时候,一样会完成Flush()的功能;并关闭流与它所指向的文件之间的联系,并释放所有的与之关联的资源。这样这个文件就与该流没有任何的关系了,可以继续被别的流来访问了。所以当流使用完毕之后一定要关闭。当流关闭之后,就不能再用之前的流对象来操作文件了,否则会出错。
10.2、字节与整数、浮点数、字符串的相互转换
在读写文件的时候,经常涉及到数值与字节的相互转换、字符串与字节之间的相互转换,下面分别来说明:
2.1、32位整数与字节的相互转换
byte[] buffer = BitConverter.GetBytes(int a);
这个是将32位的整数(我们看到的是十进制的整数,32位是指在计算机中以二进制存放的时候,占有32个bit位)转换成为长度为4的字节数组:32位整数表示的是二进制的32个bit位,正好对应着4个字节,在字节中,从低八位到高八位加起来正好也是32位,最低位是二的零次方,然后是二的一次方,依次类推,所以转换的时候,首先将十进制的32位整数按照二进制转换成32个bit位,然后按照八位进行分割,变成四个字节,分别存放到buffer中,整数的低八位存放在buffer[0],最高的八位存放在buffer[3]中,编译调试的时候,看到的每一个字节的数字又是单个字节从二进制转换成十进制的值。
int a = BitConverter.ToInt32(byte[] buffer,int startIndex)
这个是将buffer中的,从startIndex开始的四个字节,按照从低八位到高八位的顺序转换成为32位的整数值,startIndex是低八位,startIndex + 3是高八位。
2.2、64位double与字节的相互转换
byte[] buffer = BitConverter.GetBytes(double a);
这个是将64位的double转换成为长度为8的字节数组,double的低八位存放在buffer[0],最高的八位存放在buffer[7]中。
double a = BitConverter. ToDouble(byte[] buffer,int startIndex)
这个是将buffer中的,从startIndex开始的八个字节,按照从低八位到高八位的顺序转换成为64位的double值。
2.3、字符串与字节的相互转换
byte[] buffer = Encoding.Default.GetBytes(string a);
这个是将一个字符串转换成为一个字节数组。
string s = Encoding.Default.GetString(byte[] buffer,int startIndex,int count);
这个是将buffer数组转换成为字符串,startIndex是buffer中的起始索引,count是buffer中的要转换的字节长度。
10.3、StreamWriter、StreamReader
在读写文件的时候,经常要读写文本文件,在读写文本文件的时候有两个类是非常方便的。
3.1、写文本文件
StreamWriter sw = newStreamWriter(@"D:\test.txt");
定义了一个StreamWriter对象之后,首先是没有文件就创建文件,有就把文件内容清空(本节中的构造函数是这样的,但是可以调用其他的重载构造函数来完成不同的功能)。
sw.WriteLine("123");
这个函数是将一个字符串写入到文件的末尾,并且自动的加上\r\n换行。
sw.Write("123");
这个是将一个字符串写入到文件的末尾,不会加\r\n换行。
注意:以上的两个函数只是写入到缓冲区了,但是此时去查看文件,文件还是没有被写入的。
sw.Flush();
这个是将缓冲区的内容写入到基础流中,并清空缓冲区,这样才可以在文件中查看到内容。只要文件没有被关闭,就可以不停的Write和Flush,后面的写入是往文件末尾写。 如果缓冲区很大,可以Write一部分然后Flush一部分,分多次Write和Flush。
sw. Close()
这个是将缓冲区的内容写入到基础流中,并清空缓冲区,所以即使没有Flush(),调用的时候,一样会完成Flush()的功能;并关闭流与它所指向的文件之间的联系,并释放所有的与之关联的资源。这样这个文件就与该流没有任何的关系了,可以继续被别的流来访问了。所以当流使用完毕之后一定要关闭。当流关闭之后,就不能再用之前的流对象来操作文件了,否则会出错。
3.2、读文本文件
StreamReader sr = newStreamReader(@"D:\test.txt",Encoding.Default);
string str = sr.ReadLine();
这个函数是读取文本文件中的某一行,并将该行后面的\r\n删除掉,返回字符串,如果该行后面没有\r\n,那么就直接返回该行。读取总是从上一次读取之后再读取,如果读完了所有的行,再次读取就会返回null了。
string str = sr.ReadToEnd();
读取整个文本文件的,以字符串的形式返回。
注意:最后操作完成后要关闭流。文件如果不存在,那么就会在定义的时候异常。
10.4、File
该类是一个与文件有关的类,是一个静态类,它里面的函数是静态函数。主要提供了对文件的创建、删除、移动、打开文件的静态方法。
4.1、文件拷贝
File.Copy(string sourceFileName,string destFileName)
两个参数包含完整的路径和文件名,目标路径中不能有与destFileName同名文件,否则会异常。
File.Copy(string sourceFileName,string destFileName,bool overwrite)
前两个参数包含完整的路径和文件名,最后一个参数指示如果在目标路径中有同名文件,是否覆盖这个文件。
4.2、判断文件是否存在
File.Exists(string path);
bool isExist = File.Exists(@"c:\zwj");
如果path指定的文件还存在,那么就返回true,否则返回false
4.3、删除文件
File.Delete(string path);
删除path指定的文件。如果存在就删除,不存在也不会引发异常。
4.4、文件移动
File.Move(string sourceFileName,string destFileName)
两个参数包含完整的路径和文件名,目标路径中不能有与destFileName同名文件,否则会异常。
4.5、创建文件
File.Create(string path);
如果文件不存在,那么就创建;如果存在,且为可写,那么就覆盖;如果存在不可写,那么就会发生异常。
默认情况下,新创建的文件是可读写的。
4.6、与文件有关的几个时间
File.GetCreationTime(string path);
这个函数是获取文件的创建时间,返回DateTime。
File.GetLastAccessTime(string path);
这个函数是获取最后一次访问文件的时间,返回DateTime。
File.GetLastWriteTime(string path);
这个函数是获取文件的最后一次的修改时间,返回DateTime。
10.5、FileInfo类
该类也是一个与文件有关的类,但是该类不是一个静态类,该类必须经过实例化之后才可以使用。
FileInfo fi = newFileInfo(string fileName);
5.1、复制
fi.CopyTo(string destFileName)
目标路径中不能有与destFileName同名文件,否则会异常。
fi.CopyTo(string destFileName, bool overwrite)
最后一个参数指示如果在目标路径中有同名文件,是否覆盖这个文件。
注意:以上两个函数会返回FileInfo对象,这个对象是指向destFileName的。
5.2、删除
fi.Delete();
永久删除文件,文件不存在也不会发生异常。
5.3、移动
fi.MoveTo(string destFileName)
目标路径中不能有与destFileName同名文件,否则会异常。
5.4、属性
d:\programmfile\zwj.txt
1、 CreationTime:获取或者设置文件的创建时间。返回 DateTime。
2、 LastWriteTime:最后一次的修改时间。返回 DateTime。
3、 LastAcessTime:最后一次的访问时间。返回 DateTime。
4、 DirectoryName:获取包含该文件的目录的完整路径的字符串:d:\programmfile
5、 FullName:获取包括文件名的完整路径:d:\programmfile\zwj.txt
6、 Name:获取文件名和扩展名:zwj.txt
7、 Extension:获取文件的扩展名:.txt
8、 Exists:指示文件是否存在,存在返回true,否则为false。
9、 IsReadOnly:获取或者设置当前的文件是否为只读的。是返回true,否则为false。
10、Length:返回文件的长度,以字节为单位。长整型。
11、Directory:获取包含该文件的目录的DirectoryInfo实例。
0.6、关于文件的总结
1、不论是创建文件还是读取文件,只要文件的路径只是一个文件名,而没有绝对的路径,那么文件名默认为当前目录下面。这个当前是可能在变化的,程序开始启动的时候,当前肯定是exe同一级目录下面,但是有时候程序启动之后,再进行某些操作的时候,比如导出到excle报表,选择了别的文件夹路径,那么这个当前路径就是新路径而非exe同一级目录了。
2、..\:这个表示当前目录的上一级目录。这个当前目录像上面一样也是可能变化的。
3、当对一个文件第一次进行写操作的时候,首先会把文件的内容清空。
4、如果要想找到一个固定的exe文件的路径:
Application.ExecutablePath:获取启动应用程序可执行文件的路径,包括可执行文件的名称:
E: \source\存储过程版\PVPT\MainMenu\bin\Debug\MainMenu.EXE
Application.StartupPath:获取启动应用程序可执行文件的路径,不包括可执行文件的名称:
E: \source\存储过程版\PVPT\MainMenu\bin\Debug(没\)
0.7、Directory
该类是一个与目录有关的类,是一个静态类,它里面包含了静态函数和静态属性。
1、 创建目录
Directory.CreateDirectory(string path);
按照path所指定的路径去创建目录、子目录,返回一个指向path的DirectoryInfo对象。这里是创建目录而非文件,不能通过"r:"创建一个逻辑盘,只能在现有的逻辑盘下面创建目录。重复创建已经存在的目录是不会发生异常的。任何新创建的目录名不能与该目录同一级目录下的文件名同名,否则就会报错。
2、 删除目录
Directory.Delete(string path);
这个函数是删除目录,这个目录必须为空目录,即里面不能有目录和文件。否则异常。目录不存在也会异常。
Directory.Delete(string path,bool recursive);
这个函数是删除目录,当recursive为true,那么可以删除非空目录,否则只能删除空目录。目录不存在也会异常。
3、 移动目录
Void Directory.Move(string sourceDirName,string destDirName)
将原目录的内容全部移到目标目录里面,然后将原目录删除。
4、 判断目录是否存在
Directory.Exists(string path);
存在返回true,否则返回false。
5、 得到目录的创建时间:DateTime Directory.GetCreationTime(string path);
6、 得到目录的最后一次访问时间:DateTime Directory.GetLastAccessTime(string path)
7、 得到目录的最后一次修改时间:DateTime Directory.GetLastWriteTime(string path)
8、 得到当前的执行程序所在的目录:string Directory.GetCurrentDirectory(string path);
9、 string[] Directory.GetDirectories(string path);
获取指定目录中的子目录名称,只是获取指定目录的下一级,而非下两级或者下三级。返回的是包括目录名在内的完整的路径字符串数组。
10、 string[] Directory.GetFiles(string path);
获取指定目录中的子文件名称,只是获取指定目录的下一级,而非下两级或者下三级。返回的是包括文件名在内的完整的路径字符串数组。
11、 string Directory.GetDirectoryRoot(string path)
获取path执行的路径的根目录,卷信息,比如”d:\”,path可以是文件或者目录
12、 string[] Directory.GetLogicalDrives();
得到本系统的逻辑盘,以字符串的形式返回。如:”d:\”
13、 DirectoryInfo Directory.GetParent(string path);
14、 得到指定的path的父目录,返回DirectoryInfo对象,path可以是文件和目录。如果path是根目录,那么就返回null。
注意:目录最后不要加”\”
10.8、DirectoryInfo类
该类也是一个与目录有关的类,但是该类不是一个静态类,该类必须经过实例化之后才可以使用。
DirectoryInfo di = newDirectoryInfo(@"d:\Program Files");
10.8.1、属性
1、CreationTime:获取或者设置目录的创建时间。返回 DateTime。
2、LastWriteTime:最后一次的修改时间。返回 DateTime。
3、LastAcessTime:最后一次的访问时间。返回 DateTime。
4、Exists:指示目录是否存在,存在返回true,否则为false。
5、FullName:获取目录的完整路径,返回string。
6、Extension:获取目录名的扩展名,返回string。
7、Name:不包含路径的目录名,即文件夹的名字,返回string。
8、Parent:获取父目录,如果当前目录是根目录,就返回null,否则返回DirectoryInfo。
9、Root:获取当前目录的根目录,返回DirectoryInfo。
10.8.2、方法
1、DirectoryInfo di.CreateSubdirectory(string path)
这个是在当前目录的下面创建子目录、子子目录,返回一个新的DirectoryInfo对象。比如path:a\b\c,a前面不能有\。
2、void di.Delete()
这个表示删除di指定的空目录,如果目录中有内容,那么会引发异常。
void di.Delete(bool)
如果目录不为空,那么true表示全部删除,false表示不删除。如果目录为空,不论true还是false都将直接删除。
3、DirectoryInfo [] di.GetDirectories();
返回当前目录的下一级所有子目录。如果没有,返回的数组长度为0.
4、FileInfo[] di.GetFiles();
返回当前目录的下一级所有子文件。如果没有,返回的数组长度为0.
10.9、Path
该类是一个与路径相关的类,是一个处理路径字符串的,它不会去识别文件或者目录,它只会去识别字符串,然后单纯的按照字符串来处理,它里面的函数全部是public static。
1、Path.ChangeExtension(string path,string extension);
这个函数是用来更新扩展名的,path为完整的路径,extension是含有签到句点的扩展名。如果path没有扩展名,那么就加上extension,有就直接更新。如果extension为null或者空字符串,那么会将path中的扩展名去掉。这个只是对字符串进行变换,对原文件不会有任何的影响。
2、Path.GetDirectoryName(string path);
path为文件或者目录的路径,这个是返回文件或者目录的上一级目录,即返回的是最后一个”\”符号之前的字符串,如果path为null或者为根目录,那么就返回null。
3、Path.GetExtension(string path);
返回指定路径的带前导句点的扩展名。如果没有扩展名,就返回长度为0的字符串。如果path为null就返回null。
4、Path.GetFileName(string path);
获取包括扩展名在内的文件名,如果是目录,也会当成文件处理。path如果为根目录则返回长度为0的字符串;如果为null则返回null。
1、Path.GetFileNameWithoutExtension(string path);
获取不包括扩展名在内的文件名,如果是目录,也会当成文件处理。path如果为根目录则返回长度为0的字符串;如果为null则返回null。
2、Path.GetFullPath(string path);
path为相对路径或者绝对路径,返回绝对路径。
3、Path.GetPathRoot(string path)
获取path的根目录信息,如果如果path为null,那么就返回null。
10.10、小结
注意:任何一个文件都有三个时间:
创建时间:这个文件的创建时间,如果把这个文件复制到另外一个路径下面,那么文件的创建时间就发生了变化,是复制时的时间;如果对这个文件改名,那么创建时间不会发生改变。
最后一次的修改时间:这个时间指的是最后一次修改文件内容的时间。如果把这个文件复制到另外一个路径下面,那么文件的最后一次修改时间就不会发生变化;如果对这个文件改名,那么最后一次修改时间也不会发生改变。只有修改内容才会发生变化。
最后一次的访问时间:是最后一次访问这个文件的时间,这个时间比较模糊。
如果用一个文件A去覆盖同名文件B,那么B被覆盖后,B的创建时间不变,最后一次的修改时间变成了A的最后一次修改时间。最后一次修改一个文件的时候,如果不保存,那么修改时间还是原来的时间。
10.11、关于文件操作的程序案例
10.11.1、文件拷贝
我在网上下载了一万多首歌曲,然后对这些歌曲分别按照歌手的名称新建了文件夹,然后将这些歌曲存放在对应的文件夹中,有些文件夹中的歌曲又按照该歌手的歌曲产生年份,分类存放在该文件夹的年份子文件夹中,如上图。这样最终导致文件夹有几百个,这些文件夹都存放在一个父级文件夹MUSIC中,我现在想把所有文件夹中的歌曲全部拷贝到另外一个文件夹中,如果一个一个文件夹的拷贝,太难了,所以就产生了下面的程序:
namespace FileCopy
{
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
}
///<summary>
///将源目录下面的所有直接文件全部都拷贝到目标目录下面
///</summary>
///<param name="sourceDirectoryPath">源目录路径</param>
///<param name="objectDirectoryPath">目标目录路径</param>
public void CopyFiles(string sourceDirectoryPath,string objectDirectoryPath)
{
//获取源目录下面的所有文件
string[] sourceFilePath = Directory.GetFiles(sourceDirectoryPath);
//循环拷贝源目录下面的文件到目标目录下面
for (int i = 0; i < sourceFilePath.Length; i++)
{
//将源文件实例化为FileInfo对象
FileInfo fi = new FileInfo(sourceFilePath[i]);
//将源文件拷贝到目标文件夹下面,不修改文件名
File.Copy(sourceFilePath[i], objectDirectoryPath + "\\" + fi.Name,true);
}
}
///<summary>
///将目录下面的所有文件包含子目录下面的文件全部都拷贝到目标目录下面
///</summary>
///<param name="sourceDirectoryPath"></param>
///<param name="objectDirectoryPath"></param>
public void FileCopy(string sourceDirectoryPath, string objectDirectoryPath)
{
//将源目录下面的所有直接文件全部都拷贝到目标目录下面
CopyFiles(sourceDirectoryPath, objectDirectoryPath);
//获取指定目录的一级子目录的集合
string[] subSourceDirectoryPath = Directory.GetDirectories(sourceDirectoryPath);
//递归结束条件
if (subSourceDirectoryPath.Length <= 0)
{
return;
}
//递归拷贝文件夹中的文件
for (int i = 0; i < subSourceDirectoryPath.Length; i++)
{
FileCopy(subSourceDirectoryPath[i], objectDirectoryPath);
}
}
///<summary>
///获取源目录下面的所有直接文件总长度
///</summary>
///<param name="sourceDirectoryPath"></param>
///<returns></returns>
long GetFileOfDirectoryLength(string sourceDirectoryPath)
{
//定义一个文件长度
long fileLength = 0;
//获取源目录下面的所有文件
string[] sourceFilePath = Directory.GetFiles(sourceDirectoryPath);
//循环源目录下面的文件
for (int i = 0; i < sourceFilePath.Length; i++)
{
//将源文件实例化为FileInfo对象
FileInfo fi = new FileInfo(sourceFilePath[i]);
//长度加和
fileLength = fileLength + fi.Length;
}
return fileLength;
}
///<summary>
///获取源目录下面的所有文件的总长度
///</summary>
///<param name="sourceDirectoryPath"></param>
///<returns></returns>
long GetAllFileOfDirectoryLength(string sourceDirectoryPath)
{
//定义一个文件长度
long fileLength = 0;
fileLength = fileLength + GetFileOfDirectoryLength(sourceDirectoryPath);
//获取指定目录的一级子目录的集合
string[] subSourceDirectoryPath = Directory.GetDirectories(sourceDirectoryPath);
//递归结束条件
if (subSourceDirectoryPath.Length <= 0)
{
return fileLength;
}
//递归获取文件夹中的文件长度
for (int i = 0; i < subSourceDirectoryPath.Length; i++)
{
fileLength = fileLength + GetAllFileOfDirectoryLength(subSourceDirectoryPath[i]);
}
return fileLength;
}
private void btn_Copy_Click(object sender, EventArgs e)
{
if (Directory.Exists(txt_SourceDirectoryPath.Text.Trim()) == false)
{
MessageBox.Show("源文件路径不存在!");
return;
}
if (Directory.Exists(txt_ObjectDirectoryPath.Text.Trim()) == false)
{
MessageBox.Show("目标文件路径不存在!");
return;
}
if (txt_SourceDirectoryPath.Text.Trim().Contains(txt_ObjectDirectoryPath.Text.Trim()) == true)
{
MessageBox.Show("目标路径不能与源路径一样或者被源路径包含!");
return;
}
FileCopy(txt_SourceDirectoryPath.Text.Trim(), txt_ObjectDirectoryPath.Text.Trim());
MessageBox.Show("拷贝成功!");
}
}
}
10.11.2、照片在数据库中的存储
在做项目的时候,经常要将照片上传保存到数据库中,然后再从数据库中下载下来。这个涉及到文件的转换以及数据库的存储。
现在有一张jpg格式的照片,在sqlserver数据库中有一张ImageRelation表,表里面有一个image类型的字段imageInfo,现在想把照片存放在这个字段中,然后再读取出来。image类型将会存放一张图片,图片在数据库中是以字节数组的形式存放的,在C#中,我们首先会将图片转换成为字节数组,但是字节数组没法与sql语句结合去拼凑成sql server识别的命令。那么应该怎么处理呢?请看下面的代码:
(1)、首先将照片文件转换成为字节数组
///<summary>
///将图片转换成为字节数组
///</summary>
///<param name="imageFilePath"></param>
///<returns></returns>
public byte[] ConvertImageFileToByteArray(string imageFilePath)
{
byte[] buffer = new byte[1024 * 1024 * 20];//20M
Stream fsr = new FileStream(imageFilePath, FileMode.Open, FileAccess.Read, FileShare.Read);
int length = fsr.Read(buffer, 0, 1024 * 1024 * 20);
byte[] imageInfo = new byte[length];
for (int i = 0; i < length; i++)
{
imageInfo[i] = buffer[i];
}
fsr.Close();
return imageInfo;
}
(2)、写Insert语句
因为字节数组没法与sql语句结合去拼凑成sql server识别的命令,所以在此处的sql语句中,用一个sql里面的变量@imageInfo来接受C#传过来的字节数组,并将@imageInfo插入到数据库。
string sqlInsertImageRelation1 = "insert into ImageRelation (code18,codeColor,imageName,imageInfo,lineUpdateFlag,addName,addTime) values('{0}','{1}','{2}',@imageInfo,'{3}','{4}',GETDATE())";
(3)、C#调用ADO.NET插入数据
string sql = string.Format(sqlInsertImageRelation1, code18, codeColor, imageName, lineUpdateFlag, addName);
SqlParameter p = new SqlParameter("@imageInfo", SqlDbType.Image);
p.Direction = ParameterDirection.Input;
p.Value = imageInfo; // imageInfo是字节数组
SqlConnection sqlConn = new SqlConnection();
sqlConn.ConnectionString = SqlConnectionString;
sqlConn.Open();
SqlCommand sqlComm = new SqlCommand();
sqlComm.Connection = sqlConn;
sqlComm.CommandText = sql;
sqlComm.Parameters.Add(p);
sqlComm.ExecuteNonQuery();
sqlConn.Close();
(4)、查询Image字段并转换成图片写到硬盘上
string sqlSelectImageRelation = "select * from ImageRelation where 2>1 "
// ds是查询出来的数据集
byte[] buffer = (byte[])ds.Tables[0].Rows[0]["imageInfo"];
Stream fsw = new FileStream(imageFilePath, FileMode.Create, FileAccess.Write, FileShare.Read);
fsw.Write(buffer, 0, buffer.Length);
fsw.Flush();
fsw.Close();