使用c#确定文件是否存在并解析UNC路径

时间:2009-01-19 17:10:54

标签: c# filesystems

我正在尝试编写一个函数来确定文件是否存在。这两种方法证明返回不一致的结果(fileExists()似乎提供了准确的结果,与isFileFound()相比,它返回​​误报 - 我试图创建实例时会发生异常)。

protected bool isFileFound(string path, string fileName)
    {
        System.IO.FileInfo fi = null;

        bool found = false;
        try
        {
            fi = new System.IO.FileInfo(path + fileName);
            found = true;
        }
        catch (Exception e)
        {
            baselogger.Fatal(e.Message + " " + e.StackTrace + " \n" + path + fileName);
        }

        return found;
    }

    protected bool fileExists(string path, string pattern)
    {
        bool success = false;

        try
        {
            success = File.Exists(path + pattern);
        }
        catch (Exception e)
        {
            baselogger.Warn(e.Message + " " + e.StackTrace + " " + e.Source);
        }

        return success;
    }

似乎无法解析以下语法的UNC路径: \\ abcserver \ c $ \ xyzfolder \ foo.bar

任何想法为什么这些方法的unc路径失败都将非常感激。

6 个答案:

答案 0 :(得分:37)

您可以为不存在的文件创建FileInfo。但是,您可以检查FileInfo.Exists属性以确定该文件是否存在,例如:

FileInfo fi = new FileInfo(somePath);
bool exists = fi.Exists;

<强>更新: 在一个简短的测试中,这也适用于UNC路径,例如像这样:

FileInfo fi = new FileInfo(@"\\server\share\file.txt");
bool exists = fi.Exists;

您确定该帐户(运行您的应用程序)可以访问该共享。我认为(默认情况下)管理权限是访问共享“c $”所必需的。

答案 1 :(得分:13)

看到这个问题:
how can you easily check if access is denied for a file in .NET?

该问题的简短版本是您没有,因为文件系统是易变的。只是尝试打开文件并在失败时捕获异常。

isFileFound方法不起作用的原因是因为您使用的FileInfo结构也可用于创建文件。您可以使用所需信息创建一个FileInfo对象,调用它的.Create()方法,然后一次性设置所需的属性。

我怀疑UNC路径失败的原因是1)从运行您的应用的用户访问管理员共享的权限问题, 2)$符号抛出方法off,要么是因为它没有正确输入,要么是因为底层.Exists()实现中的错误。

更新:

当我发布此建议时,我几乎总是会收到关于异常性能的投诉。我们来谈谈这个。是的,处理异常非常昂贵:非常昂贵。在编程中你可以做的事情很少。但是你知道那些东西是什么吗?磁盘和网络I / O.这是一个链接,演示了磁盘I / O和网络I / O成本:

  

https://gist.github.com/jboner/2841832

Latency Comparison Numbers
--------------------------
L1 cache reference                            0.5 ns
Branch mispredict                             5   ns
L2 cache reference                            7   ns             14x L1 cache
Mutex lock/unlock                            25   ns
Main memory reference                       100   ns             20x L2 cache, 200x L1 cache
Compress 1K bytes with Zippy              3,000   ns
Send 1K bytes over 1 Gbps network        10,000   ns    0.01 ms
Read 4K randomly from SSD*              150,000   ns    0.15 ms
Read 1 MB sequentially from memory      250,000   ns    0.25 ms
Round trip within same datacenter       500,000   ns    0.5  ms
Read 1 MB sequentially from SSD*      1,000,000   ns    1    ms  4X memory
Disk seek                            10,000,000   ns   10    ms  20x datacenter roundtrip
Read 1 MB sequentially from disk     20,000,000   ns   20    ms  80x memory, 20X SSD
Send packet CA->Netherlands->CA     150,000,000   ns  150    ms

如果在纳秒内思考不是你的事,那么这是另一个将一个CPU周期标准化为1秒并从那里扩展的链接:

  

http://blog.codinghorror.com/the-infinite-space-between-words/

1 CPU cycle             0.3 ns      1 s
Level 1 cache access    0.9 ns      3 s
Level 2 cache access    2.8 ns      9 s
Level 3 cache access    12.9 ns     43 s
Main memory access      120 ns      6 min
Solid-state disk I/O    50-150 μs   2-6 days
Rotational disk I/O     1-10 ms     1-12 months
Internet: SF to NYC     40 ms       4 years
Internet: SF to UK      81 ms       8 years
Internet: SF to AUS     183 ms      19 years
OS virt. reboot         4 s         423 years
SCSI command time-out   30 s        3000 years
Hardware virt. reboot   40 s        4000 years
Physical system reboot  5 m         32 millenia

即使是异常的最佳情况,您也可以在等待来自磁盘的第一个响应时访问内存至少480次,而且假设SSD速度非常快。我们中的许多人仍然需要旋转硬盘,事情变得更糟,更糟糕。

这只是故事的开头。当您使用.Exists()时,会产生额外的费用(这是一个额外的内容:您必须在打开文件时再次执行相同的工作)每个< / em>尝试。无论文件是否存在,您都要支付这笔费用,因为磁盘仍然需要在文件表中查找。使用异常方法,您只需支付在失败的情况下展开调用堆栈的额外成本。

换句话说,是的:例外情况非常昂贵。但与磁盘检查相比,它仍然更快:而不仅仅是一小部分。值得庆幸的是,这不太可能推动您的应用程序的一般性能...但我仍然想要为这项特定任务上台“异常缓慢”的论点。

答案 2 :(得分:3)

这可能是也可能不是,但您可能会因为某个案例而错误地加入您的路径文件名。

此:

success = File.Exists(path + pattern);

VS

success = File.Exists(Path.Join(path,pattern));

答案 3 :(得分:1)

这可以帮到你:
http://www.codeplex.com/FileDirectoryPath
它是 NDepend.Helpers.FilePathDirectory ,其中包含一个“路径有效性检查API”,可能很有用。

答案 4 :(得分:1)

所以我选择了

bool success = File.Exists(path + Filename);

选项,而不是使用FileInfo路由。

感谢所有的建议!

答案 5 :(得分:1)

编辑:我刚刚意识到file.exists正常工作。这绝对是首选的方法。如果应在不同的域帐户下访问共享,则下面的代码将为您提供让Windows提示用户进行身份验证的选项。有一天可能会帮助某人,所以我会把代码放在这里。

如果您需要使用其他凭据访问UNC路径或管理员共享:MSDN

要使用WNetAddConnection2进行引导,请使用以下代码:

using System;
using System.Runtime.InteropServices;

namespace Win32Api
{
    public enum ResourceScope
    {
        RESOURCE_CONNECTED = 1,
        RESOURCE_GLOBALNET,
        RESOURCE_REMEMBERED,
        RESOURCE_RECENT,
        RESOURCE_CONTEXT
    };

    public enum ResourceType
    {
        RESOURCETYPE_ANY,
        RESOURCETYPE_DISK,
        RESOURCETYPE_PRINT,
        RESOURCETYPE_RESERVED = 8
    };

    [Flags]
    public enum ResourceUsage
    {
        RESOURCEUSAGE_CONNECTABLE = 0x00000001,
        RESOURCEUSAGE_CONTAINER = 0x00000002,
        RESOURCEUSAGE_NOLOCALDEVICE = 0x00000004,
        RESOURCEUSAGE_SIBLING = 0x00000008,
        RESOURCEUSAGE_ATTACHED = 0x00000010,
        RESOURCEUSAGE_ALL = (RESOURCEUSAGE_CONNECTABLE |
                             RESOURCEUSAGE_CONTAINER | RESOURCEUSAGE_ATTACHED),
    };

    public enum ResourceDisplayType
    {
        RESOURCEDISPLAYTYPE_GENERIC,
        RESOURCEDISPLAYTYPE_DOMAIN,
        RESOURCEDISPLAYTYPE_SERVER,
        RESOURCEDISPLAYTYPE_SHARE,
        RESOURCEDISPLAYTYPE_FILE,
        RESOURCEDISPLAYTYPE_GROUP,
        RESOURCEDISPLAYTYPE_NETWORK,
        RESOURCEDISPLAYTYPE_ROOT,
        RESOURCEDISPLAYTYPE_SHAREADMIN,
        RESOURCEDISPLAYTYPE_DIRECTORY,
        RESOURCEDISPLAYTYPE_TREE,
        RESOURCEDISPLAYTYPE_NDSCONTAINER
    };

    [StructLayout(LayoutKind.Sequential)]
    public class NetResource
    {
        public ResourceScope Scope;
        public ResourceType Type;
        public ResourceDisplayType DisplayType;
        public ResourceUsage Usage;
        public string LocalName;
        public string RemoteName;
        public string Comment;
        public string Provider;
    };

    [Flags]
    public enum AddConnectionOptions
    {
        CONNECT_UPDATE_PROFILE = 0x00000001,
        CONNECT_UPDATE_RECENT = 0x00000002,
        CONNECT_TEMPORARY = 0x00000004,
        CONNECT_INTERACTIVE = 0x00000008,
        CONNECT_PROMPT = 0x00000010,
        CONNECT_NEED_DRIVE = 0x00000020,
        CONNECT_REFCOUNT = 0x00000040,
        CONNECT_REDIRECT = 0x00000080,
        CONNECT_LOCALDRIVE = 0x00000100,
        CONNECT_CURRENT_MEDIA = 0x00000200,
        CONNECT_DEFERRED = 0x00000400,
        CONNECT_RESERVED = unchecked((int)0xFF000000),
        CONNECT_COMMANDLINE = 0x00000800,
        CONNECT_CMD_SAVECRED = 0x00001000,
        CONNECT_CRED_RESET = 0x00002000
    }

    public static class NativeMethods
    {
        [DllImport("mpr.dll", EntryPoint = "WNetAddConnection2")]
        public static extern int WNetAddConnection2(
            NetResource netResource, string password,
            string username, AddConnectionOptions options);

        [DllImport("mpr.dll")]
        public static extern int WNetCancelConnection2(string name, int flags,
        bool force);

    }
}