文件和流(文件系统)

       大多数 Web 应用程序依赖数据库来存储信息。在多用户场景中,数据库是无与伦比的。但还是会有不可避免的遇到访问存储在其他地方(如文件系统)的数据的问题。常见的示例有:读取其他程序产生的信息、为测试而编写的临时日志等。

 

使用文件系统

       最基本的文件访问包括获取现有文件和目录的信息,以及执行典型的文件系统操作,如复制文件和创建目录。这些任务并没有设计真正的打开或写文件。

       .NET 提供了几个基类(System.IO 命名空间中)用于获取文件系统信息:

  • Directory 和 File :这两个类提供一组静态方法,可以通过它们获取任意服务器上可见的文件和目录的信息。
  • DirectoryInfo、FileInfo、DriveInfo :这些类用相似的实例方法获取同样的信息。

       这两组类提供相似的方法和属性。一般而言,Directory 和 File 类更适合处理一次性任务。DirectoryInfo 和 FileInfo 更适合获取若干信息,这样不必再每次调用方法时都提供文件或目录的信息。同时,它们还更快一些,因为 DirectoryInfo 和 FileInfo 类只执行一次安全检查(创建对象的实例时),而 Directory 和 File 在每次调用方法时都需要执行安全检查。

 

Directory 类和 File 类

       它们提供了很多有用的方法,每个方法都接受相同的参数:用于识别执行操作的文件和目录的完全限定路径名

Directory 方法:

CreateDirectory()创建一个或一组目录(创建不存在的目录中的子目录时)。
Delete()删除对应的空目录。如果要删除该目录及其所有内容(子目录和文件),第二个可选参数设为 True
Exists()返回 bool 值表示指定的目录是否存在
GetCreationTime()
GetLastAccessTime()
GetLastWriteTime()
返回一个 DateTime 对象,代表创建、访问、写入目录的时间。每个Get方法都有一个对应的Set方法,这里没有列出。
GetDirectories()
GetFiles()
返回一个字符串数组,数组元素代表指定目录中的子目录或文件。第二个可选参数支持搜索表达式,如 (ASP*.*)
GetLogicaDrives()返回一个字符串数组,数组元素代表当前计算机上定义的一个驱动器,如 c:\
GetParent()返回指定目录的父目录
GetCurrentDirectory()
SetCurrentDirectory()
设置和获取当前目录
Move()接收两个参数:源路径和目标路径。目录及其内容可以移动到任意路径,但必须在同一个磁盘
GetAccessControl()
SetAccessControl()
返回或设置一个 System.Secutiry.AccessControl.DirectorySecurity 对象。可以使用这个对象检查 Windows 访问控制表(ACL),它们应用于这个目录且可以通过编程来修改。

 

File 方法:

Copy()接收两个参数:源文件和目标文件的完整文件名。如果允许覆写,第三个可选参数设为 true
Delete()删除指定的文件,如果找不到文件也不会抛出异常。
Exists()文件是否存在
GetAttributes()
SetAttributes()
获取或设置一个可以包含 FillAttribute 枚举值的任意组合的枚举值
GetCreationTime()
GetLastAccessTime()
GetLastWriteTime()
返回一个 DateTime 对象,代表创建、访问、写入文件的时间。每个Get方法都有一个对应的Set方法,这里没有列出。
Move()接收两个参数:源文件和目标文件的完整文件名。可以跨磁盘移动文件甚至同时重命名文件
Create()
CreateText()
创建指定的文件并返回一个可用于写的 FileStream 对象。CreateText() 执行同样的任务但是返回一个封装了流的 StreamWriter 对象。
Open()
OpenText()
OpenRead()
OpenWrite()

打开一个文件(假设其存在)。OpenText() 和 OpenRead() 以只读方式打开文件,返回一个 FileStream 或 StreamReader 。OpenWrite() 以只写方式打开文件,返回 FileStream
ReadAllText()
ReadAllLines()
ReadAllBytes()
读取整个文件。分别以单一字符串、字符串数组、字节数组的形式返回内容。仅在小文件才使用这些方法,对于大型文件,使用流每次读取一块以节省内存使用。
WriteAllText()
WriteAllLines()
WriteAllBytes()

将单一字符串、字符串数组、字节数组一次写入完整文件内容。可以覆盖已存在的文件。
GetAccessControl()
SetAccessControl()
返回或设置一个 System.Secutiry.AccessControl.FileSecurity 对象。可以使用这个对象检查 Windows 访问控制表(ACL),它们应用于这个目录且可以通过编程来修改。

提示:File 类缺乏的唯一功能是获取给定文件的大小(可由 FileInfo 类提供)。

string directoryName = @"d:\AnswerQuestion";
ListBox1.DataSource = Directory.GetFiles(directoryName);
ListBox1.DataBind();
image

 

 

DirectoryInfo 类和 FileInfo 类

       DirectoryInfo 和 FileInfo 类镜像 Directory 和 File 类的功能。此外,它们还可以轻易的遍历文件和目录的关系。

       Directory 类和 File 类只提供了方法,而 DirectoryInfo 类和 FileInfo 类同时还提供了属性。DirectoryInfo 类和 FileInfo 类的另一个好处是它们共享一组属性和方法,因为它们都继承自 FileSystemInfo 基类。

DirectoryInfo 和 FileInfo 的共有成员: 

Attributes获取和设置 FileAttributes 枚举值,从而设置文件特性
CreationTime()
LastAccessTime()
LastWriteTime()

允许使用 DateTime 对象获取和设置创建、最后访问和最后写入的时间
Exists按照文件和目录是否存在返回 true 或 false
FullName
Name
Extension

返回一个字符串,分别代表完全限定名、目录或文件名(带有扩展名)、扩展名
Delete()如果文件或目录存在,则删除。删除目录时必须为空,否则指定一个为 true 的可选参数
Refresh()更新对象使其与文件系统发生的任意变化同步。
Create()创建指定的文件或目录
MoveTo()复制目录及其内容或者文件。对于 DirectoryInfo 需要指定新路径,对于 FileInfo 需要指定路径和文件名

DirectoryInfo 特有成员:

Parent 和 Root返回父目录或根目录
CreateSubdirectory()DirectoryInfo 对象代表的目录中创建一个目录,返回一个代表子目录的 DirectoryInfo 对象
GetDirectories()返回一个 DirectoryInfo 数组,代表当前目录的所有子目录
GetFiles()返回代表当前目录中所有文件的 FileInfo 对象的数组

 

FileInfo 特有成员:

Directory返回一个代表父目录的 DirectoryInfo 对象
DirectoryName返回父目录名称的字符串
Length返回长整形的文件字节数
CopyTo()复制文件到指定的新路径和文件名。返回一个代表新文件(复制的)的 FileInfo 对象
Create()
CreateText()
创建指定的文件,返回一个可用来写文件的 FileStream 或 封装了流的 StreamWriter 对象
Open()
OpenText()
OpenReader()
OpenWrite()

打开一个文件(假设存在)。OpenRead 和 OpenText 以只读方式打开文件,返回一个 FileStream 或 StreamReader 。OpenWrite 以只写方式打开文件,返回一个 FileStream
// new 对象时不一定要对应真实的物理文件或目录
DirectoryInfo myDerectory = new DirectoryInfo(@"d:\AnswerQuestion");
FileInfo myFile = new FileInfo(@"d:\AnswerQuestion\test.txt");
 
// 如果不确定,可以这样检查
if (!myDerectory.Exists)
{
    myDerectory.Create();
}
if (!myFile.Exists)
{
    // Create() 返回一个可写的文件流
    FileStream stream = myFile.Create();
    stream.Close();
}

       DirectoryInfo 和 FileInfo 对象在你第一次查询某个属性时获取来自文件系统的信息,在后续的使用中不再检查新的信息。如果此时文件发生了变化会导致不一致性。如果你知道或者怀疑数据有不一致的可能,可以调用 ReFresh()方法获取最新的信息。

       DirectoryInfo 没有提供任何获取目录大小的属性。不过你可以累加所有文件的 File.Lengh 属性:

private static long CalculateDirectorySize(DirectoryInfo directory, bool includeSubdirectories)
{
    long totalSize = 0;
    FileInfo[] files = directory.GetFiles();
    foreach (FileInfo file in files)
    {
        totalSize += file.Length;
    }
 
    if (includeSubdirectories)
    {
        DirectoryInfo[] dirs = directory.GetDirectories();
        foreach (DirectoryInfo dir in dirs)
        {
            totalSize += CalculateDirectorySize(dir, true);
        }
    }
    return totalSize;    
}

 

DriveInfo

       关于剩余空间的信息,要借助 DriveInfo 类。DriveInfo 允许你获得计算机驱动器的信息。没什么信息会让你感兴趣,除了获取磁盘已经使用的空间大小和剩余空间的大小。

TotalSize驱动器的总字节数
TotalFreeSpace磁盘剩余字节数
AvailableFreeSpace磁盘可用空间的字节数
DriveFormat驱动器所使用的文件系统名称(NTFS 或 FAT32)
DriveType返回一个 DriveType 枚举值,表明驱动器是固定的、网络的、CD-ROM等等或者返回 Unknown
IsReady返回驱动器是否已经准备好用于读或写
Name返回驱动器符号(如 C: E: )
VolumeLabel返回或设置驱动器卷标
RootDirectory返回驱动器根目录的 DirectoryInfo 对象
GetDrives()获取一个 DriveInfo 数组,代表当前计算机的所有逻辑分区

 

使用 Attributes

       FileInfo 和 DirectoryInfo 类的 Attributes 属性代表文件或目录的文件系统特性,所有它包含一组 FileAttributes 枚举值。

FileAttributes 枚举值:

Archive项已经归档。应用程序可以基于这个特性标志备份或删除的文件。
Compressed项被压缩
Device当前不使用,为以后保留的枚举关键字
Directory项是一个目录
Encrypted项被加密
Hidden项是隐藏的(但仍然可以在 Windows 资源管理器里看到它)
Normal项是普通的,没有任何特性设置。这个特性仅在单独使用时有效
NotContentIndexed项不会按照操作系统的内容索引服务来服务
Offline文件不在线且当前不可用
Readonly项是只读的
ReparsePoint文件包含再解析点,它是与NTFS文件系统中的文件或目录关联的一块用户定义的数据
SparseFile文件是稀疏文件。稀疏文件通常是由大量零组成的大型文件。这个项仅被 NTFS 支持
System项是操作系统的一部分或者只被操作系统使用
Temporary项是临时的,应用程序不使用它时可以被删除
FileInfo myFile = new FileInfo(@"d:\AnswerQuestion\TestCSharp.pdb");
 
// 调用 Attributes.ToString() 返回逗号间隔的特性列表
lblInfo.Text = myFile.Attributes.ToString();
 
if (myFile.Attributes == FileAttributes.ReadOnly)
{
    // 这个 if 块的逻辑是错误的,仅当文件只含有只读属性时才成立
}
 
if ((myFile.Attributes & FileAttributes.ReadOnly) != 0)
{
    // 判断特性是不是含有 ReadOnly,需要进行位与运算
    // 如果 == 0,那么就不包含这个特性
}
 
// 设置 只读 特性并且保留其他所有原特性
myFile.Attributes = myFile.Attributes | FileAttributes.ReadOnly;
 
// 先或后与,下面这步会移除 ReadOnly 之外所有的特性
myFile.Attributes = myFile.Attributes & FileAttributes.ReadOnly;

 

使用通配符过滤文件

       DirectoryInfo 和 Directory 都提供了一种方式:可以根据搜索表达式来匹配特定的目录或文件。* 代表0或多个任意字符 ? 代表任意单个字符:

DirectoryInfo dir = new DirectoryInfo(@"d:\AnswerQuestion");
FileInfo[] files = dir.GetFiles("*.txt");
DirectoryInfo[] dirs = dir.GetDirectories();
foreach (FileInfo file in files)
{
    // ......
}
foreach (DirectoryInfo dir in dirs)
{
    // ......
}
// GetFiles() 和 GetDirectories() 仅搜索当前目录
// 如果要包含子目录进行搜索,需要使用递归

 

获取文件的版本信息

       文件版本信息是你在资源管理器中查看 EXE 或 DLL 文件时所看到的信息。版本信息通常包括版本号、生产组件的公司名称、商标信息等。

       FileInfo 和 File 都没有提供获取文件版本信息的方法。不过可以使用 System.Diagnostics.FileVersionInfo.GetVersionInfo() 获取 FileVersionInfo 对象。

FileVersionInfo 属性:

FileVersion、FileMajorPart
FileMinorPart、FileBuildPart
FilePrivatePart

一个典型的主版本号显示格式为:【主版本号】.【次版本号】.【内部版本号】.【专用版本号】
FileName获取 FileVersionInfo 实例所描述的文件的名称
OriginalFilename获取文件创建时的名称
InternalName如果存在,则获取文件的内部名称
FileDescription获取文件的描述
CompanyName获取创建文件的公司名
ProductName获取随文件一起发布的产品的名称
ProductVersion、ProductMajorPart
ProductMinorPart、ProductBuildPart
ProductPrivatePart

获取完整产品版本号(ProductVersion)
IsDebug文件是否包含调试信息或是否在调试启用的情况下被编译
IsPatched文件是否已修改且是否不同于相同版本号的原始文件
IsPreRelease表示文件是开发版本,而不是用于商业目的的发行版本
IsPrivateBuild是否是采用标准的发行过程开发的
IsSpecialBuild文件是否为特殊的内部版本
Comments获取与文件关联的注释
Language获取版本信息块的默认语言字符串
LegalCopyright获取所有适用于指定文件的版权声明
LegalTrademarks获取应用到文件的商标和注册商标

 

 

Path 类

       如果你正在使用文件,那么也很有可能要用用到文件路径和目录路径。路径信息用普通的字符串保存。因此,有时需要字符串解析代码来操作这些字符。

       Path 类提供了若干个执行常见路径处理任务的静态辅助方法,Path.Combine()可以把一个完整的目录路径与那个目录中的任意文件名一起使用:

DirectoryInfo dirInfo = new DirectoryInfo(@"d:\AnswerQuestion");
string file = "test.txt";
string path = Path.Combine(dirInfo.FullName, file);

 

      为了防止规范化错误之类的风险,也可以使用 Path 类。

       规范化错误是一种特定类型的应用程序错误,它会在你的代码中假定用户提供的值总是符合标准格式时发生。规范化错误时一项低级但非常严重的技术,它们通常会导致用户可以执行某个本应该受到限制的动作。(一个恶名昭彰的规范化错误是 SQL 注入,其他形式的规范化错误可能发生在 路径 和 URL 中)。

       考虑下面的代码:

// 攻击者加入提供一个限定文件名,如: ..\filename
// 连接后的路径 WebApp\Documents\..\filename 将会从父目录 WepApp 获取文件
FileInfo file1 = new FileInfo(Server.MapPath("Documents\\" + txtBox.Text));
// 代码的修复其实很简单,借助一次 Path 类
// 使用 GetFileName() 仅提取字符串最终的文件名部分
string filename = Path.GetFileName(txtBox.Text);
FileInfo file2 = new FileInfo(Server.MapPath(Path.Combine("Documents", filename)));

 

       如果要处理 URL,可以用 Syestem.Uri 类型实现同样的操作。下面的代码演示了 删除查询字符串并确保它只指向给定的服务器和虚拟目录:

string uriString = "http://www.wrongsite.com/page.aspx?cmd=run";
Uri uri = new Uri(uriString);
 
// 获取 Uri 的绝对路径 page 的值现在是: page.aspx
string page = Path.GetFileName(uri.AbsolutePath);
 
// 创建正确的虚拟目录路径及合适的文件名
Uri baseUri = new Uri("http://www.rightsite.com");
uri = new Uri(baseUri, page);

 

Path 类最有用的方法:

Combine()合并文件或子目录的路径
ChangeExtension()修改字符串中当前文件的扩展名。如果未指定扩展名,当前扩展名会被删除
FetDirectoryName()返回目录信息。它是第一个和最后一个目录分隔符(\)之间的文本
GetFileName()返回路径的文件名部分
GetFileNameWithoutExtension()与 GetFileName() 相似,但返回的字符串中忽略了扩展名
GetFullPath()该方法对绝对路径无效。它按当前目录把相对路径变成绝对路径
GetPathRoot()获取根目录的字符串信息(例如 C:\),对于相对目录,返回空引用
HasExtension()如果路径以文件扩展名结束,则返回 true
IsPathRooted()如果路径是绝对路径,则返回 true,如果是相对路径,则返回 false

 

       虽然 Path 类有从目录结构向下延伸的方法(在路径中加入子目录),但它没有提供任何返回上层目录的方法(从路径中删除子目录)。不过,可以在 Combine()里使用相对路径打破这一限制,然后用 GetFullPath()让它以正常的格式返回。

string path = @"c:\temp\subdir";
path = Path.Combine(path, "..");// path = c:\temp\subdir\..
path = Path.GetFullPath(path);  // path = c:\temp
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值