2019.04.19 读书笔记 比较File.OpenRead()和File.ReadAllBytes()的差异

最近涉及到流的获取与转化,终于要还流的债了。

百度了一下,看到这样的两条回复,于是好奇心,决定看看两种写法的源码差异。

2019.04.19 读书笔记  比较File.OpenRead()和File.ReadAllBytes()的差异

先来看看OpenRead()

public static FileStream OpenRead(string path) => 
    new FileStream(path, FileMode.Open, FileAccess.Read, FileShare.Read);

//构造函数又调用了其他构造函数,继续深入
[SecuritySafeCritical]
public FileStream(string path, FileMode mode, FileAccess access, FileShare share) : this(path, mode, access, share, 0x1000, FileOptions.None, Path.GetFileName(path), false)
{
}
//调用了Init 初始化
[SecurityCritical]
internal FileStream(string path, FileMode mode, FileAccess access, FileShare share, int bufferSize, FileOptions options, string msgPath, bool bFromProxy)
{
    Win32Native.SECURITY_ATTRIBUTES secAttrs = GetSecAttrs(share);
    this.Init(path, mode, access, 0, false, share, bufferSize, options, secAttrs, msgPath, bFromProxy, false, false);
}

//读取数据到流中,过程就没什么可以看的了
[SecuritySafeCritical]
private unsafe void Init(string path, FileMode mode, FileAccess access, int rights, bool useRights, FileShare share, int bufferSize, FileOptions options, Win32Native.SECURITY_ATTRIBUTES secAttrs, string msgPath, bool bFromProxy, bool useLongPath, bool checkHost)
{
    int num;
    if (path == null)
    {
        throw new ArgumentNullException("path", Environment.GetResourceString("ArgumentNull_Path"));
    }
    if (path.Length == 0)
    {
        throw new ArgumentException(Environment.GetResourceString("Argument_EmptyPath"));
    }
    FileSystemRights rights2 = (FileSystemRights) rights;
    this._fileName = msgPath;
    this._exposedHandle = false;
    FileShare share2 = share & ~FileShare.Inheritable;
    string paramName = null;
    if ((mode < FileMode.CreateNew) || (mode > FileMode.Append))
    {
        paramName = "mode";
    }
    else if (!useRights && ((access < FileAccess.Read) || (access > FileAccess.ReadWrite)))
    {
        paramName = "access";
    }
    else if (useRights && ((rights2 < FileSystemRights.ListDirectory) || (rights2 > FileSystemRights.FullControl)))
    {
        paramName = "rights";
    }
    else if ((share2 < FileShare.None) || (share2 > (FileShare.Delete | FileShare.ReadWrite)))
    {
        paramName = "share";
    }
    if (paramName != null)
    {
        throw new ArgumentOutOfRangeException(paramName, Environment.GetResourceString("ArgumentOutOfRange_Enum"));
    }
    if ((options != FileOptions.None) && ((options & 0x3ffbfff) != FileOptions.None))
    {
        throw new ArgumentOutOfRangeException("options", Environment.GetResourceString("ArgumentOutOfRange_Enum"));
    }
    if (bufferSize <= 0)
    {
        throw new ArgumentOutOfRangeException("bufferSize", Environment.GetResourceString("ArgumentOutOfRange_NeedPosNum"));
    }
    if (((!useRights && ((access & FileAccess.Write) == 0)) || (useRights && ((rights2 & FileSystemRights.Write) == 0))) && (((mode == FileMode.Truncate) || (mode == FileMode.CreateNew)) || ((mode == FileMode.Create) || (mode == FileMode.Append))))
    {
        if (!useRights)
        {
            object[] objArray1 = new object[] { mode, access };
            throw new ArgumentException(Environment.GetResourceString("Argument_InvalidFileMode&AccessCombo", objArray1));
        }
        object[] values = new object[] { mode, rights2 };
        throw new ArgumentException(Environment.GetResourceString("Argument_InvalidFileMode&RightsCombo", values));
    }
    if (useRights && (mode == FileMode.Truncate))
    {
        if (rights2 != FileSystemRights.Write)
        {
            object[] objArray3 = new object[] { mode, rights2 };
            throw new ArgumentException(Environment.GetResourceString("Argument_InvalidFileModeTruncate&RightsCombo", objArray3));
        }
        useRights = false;
        access = FileAccess.Write;
    }
    if (!useRights)
    {
        num = (access == FileAccess.Read) ? -2147483648 : ((access == FileAccess.Write) ? 0x40000000 : -1073741824);
    }
    else
    {
        num = rights;
    }
    int maxPathLength = useLongPath ? 0x7fff : (AppContextSwitches.BlockLongPaths ? 260 : 0x7fff);
    string fullPath = Path.NormalizePath(path, true, maxPathLength);
    this._fileName = fullPath;
    if ((!CodeAccessSecurityEngine.QuickCheckForAllDemands() || AppContextSwitches.UseLegacyPathHandling) && fullPath.StartsWith(@"\\.\", StringComparison.Ordinal))
    {
        throw new ArgumentException(Environment.GetResourceString("Arg_DevicesNotSupported"));
    }
    bool flag = false;
    if ((!useRights && ((access & FileAccess.Read) != 0)) || (useRights && ((rights2 & FileSystemRights.ReadAndExecute) != 0)))
    {
        if (mode == FileMode.Append)
        {
            throw new ArgumentException(Environment.GetResourceString("Argument_InvalidAppendMode"));
        }
        flag = true;
    }
    if (CodeAccessSecurityEngine.QuickCheckForAllDemands())
    {
        FileIOPermission.EmulateFileIOPermissionChecks(fullPath);
    }
    else
    {
        FileIOPermissionAccess noAccess = FileIOPermissionAccess.NoAccess;
        if (flag)
        {
            noAccess |= FileIOPermissionAccess.Read;
        }
        if (((!useRights && ((access & FileAccess.Write) != 0)) || (useRights && ((rights2 & (FileSystemRights.TakeOwnership | FileSystemRights.ChangePermissions | FileSystemRights.Delete | FileSystemRights.Write | FileSystemRights.DeleteSubdirectoriesAndFiles)) != 0))) || ((useRights && ((rights2 & FileSystemRights.Synchronize) != 0)) && (mode == FileMode.OpenOrCreate)))
        {
            if (mode == FileMode.Append)
            {
                noAccess |= FileIOPermissionAccess.Append;
            }
            else
            {
                noAccess |= FileIOPermissionAccess.Write;
            }
        }
        AccessControlActions control = ((secAttrs != null) && (secAttrs.pSecurityDescriptor != null)) ? AccessControlActions.Change : AccessControlActions.None;
        string[] fullPathList = new string[] { fullPath };
        FileIOPermission.QuickDemand(noAccess, control, fullPathList, false, false);
    }
    share &= ~FileShare.Inheritable;
    bool flag2 = mode == FileMode.Append;
    if (mode == FileMode.Append)
    {
        mode = FileMode.OpenOrCreate;
    }
    if ((options & FileOptions.Asynchronous) != FileOptions.None)
    {
        this._isAsync = true;
    }
    else
    {
        options &= ~FileOptions.Asynchronous;
    }
    int dwFlagsAndAttributes = (int) options;
    dwFlagsAndAttributes |= 0x100000;
    int newMode = Win32Native.SetErrorMode(1);
    try
    {
        string str3 = fullPath;
        if (useLongPath)
        {
            str3 = Path.AddLongPathPrefix(str3);
        }
        this._handle = Win32Native.SafeCreateFile(str3, num, share, secAttrs, mode, dwFlagsAndAttributes, IntPtr.Zero);
        if (this._handle.IsInvalid)
        {
            int errorCode = Marshal.GetLastWin32Error();
            if ((errorCode == 3) && fullPath.Equals(Directory.InternalGetDirectoryRoot(fullPath)))
            {
                errorCode = 5;
            }
            bool flag4 = false;
            if (!bFromProxy)
            {
                try
                {
                    FileIOPermission.QuickDemand(FileIOPermissionAccess.PathDiscovery, this._fileName, false, false);
                    flag4 = true;
                }
                catch (SecurityException)
                {
                }
            }
            if (flag4)
            {
                __Error.WinIOError(errorCode, this._fileName);
            }
            else
            {
                __Error.WinIOError(errorCode, msgPath);
            }
        }
    }
    finally
    {
        Win32Native.SetErrorMode(newMode);
    }
    if (Win32Native.GetFileType(this._handle) != 1)
    {
        this._handle.Close();
        throw new NotSupportedException(Environment.GetResourceString("NotSupported_FileStreamOnNonFiles"));
    }
    if (this._isAsync)
    {
        bool flag5 = false;
        new SecurityPermission(SecurityPermissionFlag.UnmanagedCode).Assert();
        try
        {
            flag5 = ThreadPool.BindHandle(this._handle);
        }
        finally
        {
            CodeAccessPermission.RevertAssert();
            if (!flag5)
            {
                this._handle.Close();
            }
        }
        if (!flag5)
        {
            throw new IOException(Environment.GetResourceString("IO.IO_BindHandleFailed"));
        }
    }
    if (!useRights)
    {
        this._canRead = (access & FileAccess.Read) > 0;
        this._canWrite = (access & FileAccess.Write) > 0;
    }
    else
    {
        this._canRead = (rights2 & FileSystemRights.ListDirectory) > 0;
        this._canWrite = ((rights2 & FileSystemRights.CreateFiles) != 0) || ((rights2 & FileSystemRights.AppendData) > 0);
    }
    this._canSeek = true;
    this._isPipe = false;
    this._pos = 0L;
    this._bufferSize = bufferSize;
    this._readPos = 0;
    this._readLen = 0;
    this._writePos = 0;
    if (flag2)
    {
        this._appendStart = this.SeekCore(0L, SeekOrigin.End);
    }
    else
    {
        this._appendStart = -1L;
    }
}

从上面的源码可以看出,OpenRead会把无论多大的文件都读取到FileStream中,能有多大呢?FileStream.Length可是long型的,就是2的63次方了,2的40次方是1T,大约就是8MT吧,我们的常用硬盘是没这么大的了,可以说能读出天荒地老了。那么图片中的第一中写法,读取没错,但是转换int就留下了隐患,int的最大限制是2G,如果文件超过2G,那么转换就会被截取,导致读到data里的数据就不完整了。所以一定不要偷懒,由于FileStream.Read的参数是int型的,一定要判断流的长度,如果超过了int最大值,要循环read。

再来看看File.ReadAllBytes(),上源码:

[SecurityCritical]
private static byte[] InternalReadAllBytes(string path, bool checkHost)
{
    byte[] buffer;
    using (FileStream stream = new FileStream(path, FileMode.Open, FileAccess.Read, FileShare.Read, 0x1000, FileOptions.None, Path.GetFileName(path), false, false, checkHost))//这里也是和OpenRead一样,把文件全部读取进来了
    {
        int offset = 0;
        long length = stream.Length;
        if (length > 0x7fffffffL)//这里才是关键,对长度做了一个判断,如果超过了2G,就抛异常了,所以决定了这个方法的文件不能超过2G
        {
            throw new IOException(Environment.GetResourceString("IO.IO_FileTooLong2GB"));
        }
        int count = (int) length;
        buffer = new byte[count];
        while (count > 0)
        {
            int num4 = stream.Read(buffer, offset, count);
            if (num4 == 0)
            {
                __Error.EndOfFile();
            }
            offset += num4;
            count -= num4;
        }
    }
    return buffer;
}

第二种写法会导致在读取文件超过2G后抛出异常,所以使用时要确定文件的大小,否则就用OpenRead()。

所以图中两种写法都可能存在隐患的BUG。

当然,在确定了不可能超过2G的情况下,还是可以*使用的。

 

上一篇:47.C#--使用FileStream文件流来实现多媒体文件的复制


下一篇:Oracle数据库 基础SQL语句练习