none
请教关于创建文件或者文件夹的问题! RRS feed

  • 问题

  • 当在使用了File.Create(...)或者Directory.CreateDirectory(...)的时候,如果参数是不正确的PATH,那么也没有抛出任何异常,只是文件和文件夹也没有创建出来,我如何才能捕获创建失败呢?
    2009年8月5日 2:23

答案

  • string zipName = "sfasdfasd";
    string path = Path.GetFullPath(zipName); // 可见如果没有包含完整的路径,会默认为当前程序路径 + sfasdfasd

    if (!Directory.Exists(path))
    {
        // 如果传入的字符串不包含具体的路径,程序会默认以默认为当前程序路径 + sfasdfasd,创建文件夹, 
        // 文件夹的确是成功创建了
        DirectoryInfo fDirectoryInfo = Directory.CreateDirectory(path);
        Console.WriteLine(fDirectoryInfo.FullName);
    }

    知识改变命运,奋斗成就人生!
    2009年8月5日 7:48
    版主
  • 我也发现这个问题了!这个路径实际上已经被创建了,就在你的应用程序的exe文件的同一个目录下(也就是你没有指定C:\...这样的的绝对路径,就在相对路径下创建),CreateDicroctory方法内部是调用这个方法来实现的,从中你可以看到事实的确如此:
            [ResourceExposure(ResourceScope.Machine)]
            [ResourceConsumption(ResourceScope.Machine)]
            internal unsafe static void InternalCreateDirectory(String fullPath, String path
    #if !FEATURE_PAL
                , DirectorySecurity dirSecurity
    #endif
            )
            {
                int length = fullPath.Length;
     
                // We need to trim the trailing slash or the code will try to create 2 directories of the same name.
                if (length >= 2 && Path.IsDirectorySeparator(fullPath[length - 1]))
                    length--;

                int lengthRoot = Path.GetRootLength(fullPath);

    #if !PLATFORM_UNIX
                // For UNC paths that are only // or ///
                if (length == 2 && Path.IsDirectorySeparator(fullPath[1]))
                    throw new IOException(String.Format(CultureInfo.CurrentCulture, Environment.GetResourceString("IO.IO_CannotCreateDirectory"), path));
    #endif // !PLATFORM_UNIX
     
                List<string> stackDir = new List<string>();

                // Attempt to figure out which directories don't exist, and only
                // create the ones we need.  Note that InternalExists may fail due
                // to Win32 ACL's preventing us from seeing a directory, and this
                // isn't threadsafe.
     
                bool somepathexists = false;
     
                if (length > lengthRoot) { // Special case root (fullpath = X:\\)
                    int i = length-1;
                    while (i >= lengthRoot) {
                        String dir = fullPath.Substring(0, i+1);

                        if (!InternalExists(dir)) // Create only the ones missing
                            stackDir.Add(dir);
                        else
                            somepathexists = true;

                        while (i > lengthRoot && fullPath[i] != Path.DirectorySeparatorChar && fullPath[i] != Path.AltDirectorySeparatorChar) i--;
                        i--;
                    }
                }
     
                int count = stackDir.Count;

                if (stackDir.Count != 0)
                {
                    String [] securityList = new String[stackDir.Count];
                    stackDir.CopyTo(securityList, 0);
                    for (int j = 0 ; j < securityList.Length; j++)
                        securityList[j] += "\\."; // leaf will never have a slash at the end
     
                    // Security check for all directories not present only.
    #if !FEATURE_PAL
                    AccessControlActions control = (dirSecurity == null) ? AccessControlActions.None : AccessControlActions.Change;
                    new FileIOPermission(FileIOPermissionAccess.Write, control, securityList, false, false ).Demand();
    #else
                    new FileIOPermission(FileIOPermissionAccess.Write, securityList, false, false ).Demand();
    #endif
                }
     
                // If we were passed a DirectorySecurity, convert it to a security
                // descriptor and set it in he call to CreateDirectory.
                Win32Native.SECURITY_ATTRIBUTES secAttrs = null;
    #if !FEATURE_PAL
                if (dirSecurity != null) {
                    secAttrs = new Win32Native.SECURITY_ATTRIBUTES();
                    secAttrs.nLength = (int)Marshal.SizeOf(secAttrs);

                    // For ACL's, get the security descriptor from the FileSecurity.
                    byte[] sd = dirSecurity.GetSecurityDescriptorBinaryForm();
                    byte * bytesOnStack = stackalloc byte[sd.Length];
                    Buffer.memcpy(sd, 0, bytesOnStack, 0, sd.Length);
                    secAttrs.pSecurityDescriptor = bytesOnStack;
                }
    #endif
     
                bool r = true;
                int firstError = 0;
                String errorString = path;
                // If all the security checks succeeded create all the directories
                while (stackDir.Count > 0) {
                    String name = stackDir[stackDir.Count - 1];
                    stackDir.RemoveAt(stackDir.Count - 1);
                    if (name.Length > Path.MAX_DIRECTORY_PATH)
                        throw new PathTooLongException(Environment.GetResourceString("IO.PathTooLong"));
                    r = Win32Native.CreateDirectory(name, secAttrs);
                    if (!r && (firstError == 0)) {
                        int currentError = Marshal.GetLastWin32Error();
                        // While we tried to avoid creating directories that don't
                        // exist above, there are at least two cases that will
                        // cause us to see ERROR_ALREADY_EXISTS here.  InternalExists
                        // can fail because we didn't have permission to the
                        // directory.  Secondly, another thread or process could
                        // create the directory between the time we check and the
                        // time we try using the directory.  Thirdly, it could
                        // fail because the target does exist, but is a file.
                        if (currentError != Win32Native.ERROR_ALREADY_EXISTS)
                            firstError = currentError;
                        else {
                            // If there's a file in this directory's place, throw.
                            if (File.InternalExists(name)) {
                                firstError = currentError;
                                // Give the user a nice error message, but don't leak path information.
                                try {
                                    new FileIOPermission(FileIOPermissionAccess.PathDiscovery, GetDemandDir(name, true)).Demand();
                                    errorString = name;
                                }
                                catch(SecurityException) {}
                            }
                        }
                    }
                }

                // We need this check to mask OS differences
                // Handle CreateDirectory("X:\\foo") when X: doesn't exist. Similarly for n/w paths.
                if ((count == 0) && !somepathexists) {
                    String root = InternalGetDirectoryRoot(fullPath);
                    if (!InternalExists(root)) {
                        // Extract the root from the passed in path again for security.
                        __Error.WinIOError(Win32Native.ERROR_PATH_NOT_FOUND, InternalGetDirectoryRoot(path));
                    }
                    return;
                }
     
                // Only throw an exception if creating the exact directory we
                // wanted failed to work correctly.
                if (!r && (firstError != 0)) {
                    __Error.WinIOError(firstError, errorString);
                }
            }

    周雪峰
    • 已标记为答案 寒武纪 2009年8月6日 1:50
    2009年8月5日 8:10
    版主

全部回复

  • 1,创建前需要使用 File.Exists(), Directory.Exists 来判断指定的路径是否存在,如果不存在再创建
    2. 如果当前用户的权限不够,可以在下面的文件夹中创建你的文件和文件夹 C:\Documents and Settings\用户名\Application Data

    知识改变命运,奋斗成就人生!
    2009年8月5日 2:30
    版主
  • 谢谢您的回答.再请教.
    1.我前面都加了判断,确保文件或者文件夹没有存在的情况下才创建的,具体的说,我画面有个入力框,预想是应该输入D:\20080805,但是我现在乱输入,比如wsdfasdfa这样,DEBUG的时候发现File.Create(wsdfasdfa)或者Directory.CreateDirectory(wsdfasdfa)都没有抛出异常,我不知道怎么判断创建失败呢?
    2.我登陆的用户是administrator的权限.
    2009年8月5日 2:36
  • string path = "wsdfasdfa";
    
    // 不正确的路径或不存在的路径 File.Exists 都会返回 false
    if (!File.Exists(path))
    {
        File.Create(path);
    }

    知识改变命运,奋斗成就人生!
    2009年8月5日 4:29
    版主
  • 你好!
         如果指定的路径不存在,会抛出 DirectoryNotFoundException异常,你用try...catch的方法捕获这个异常就可以了!
    周雪峰
    2009年8月5日 4:45
    版主
  • 另外路径格式错误等也会抛出相应的异常
    具体可以参考:
    http://msdn.microsoft.com/zh-cn/library/d62kzs03.aspx
    周雪峰
    2009年8月5日 4:46
    版主
  •        感谢您的回复,因为我想做个项目中验证某个DLL被多少Solution调用的小工具,最后把这些Solution都COPY到一个文件夹下并且打包ZIP文件,画面可以让用户指定一个存放的路径,所以希望
    在所有程序之前先验证PATH的正确性,虽然Exists返回FALSE代表PATH错误或者是文件夹不存在,但是我确实想区分开错误的PATH和正确的PATH,毕竟PATH如果是对的,那么创建一个就可以.
    下面的思路不知道哪里错误了?
           zipName = "sfasdfasd";  
              if (!Directory.Exists(zipName))
                {
                    Directory.CreateDirectory(zipName);
                    if (!Directory.Exists(zipName))
                    {
                        return;
                    }
                }
                
            上述代码DEBUG验证了一下,第一个Directory.Exists验证sfasdfasd的PATH不存在,然后CreateDirectory了,下面再次验证时候却返回认为sfasdfasd的PATH是存在的了??
           
    TO:周雪峰 你好:
                  我使用的FrameWork是2.0版本的,Directory的创建用try...catch...无法捕获的异常.....

    2009年8月5日 5:41
  • Directory的确不会抛那个异常!这个类似乎不认为sfasdfasd这样的路径是非法的!
    建议你使用正则表达式来匹配路径!

    周雪峰
    2009年8月5日 7:47
    版主
  • string zipName = "sfasdfasd";
    string path = Path.GetFullPath(zipName); // 可见如果没有包含完整的路径,会默认为当前程序路径 + sfasdfasd

    if (!Directory.Exists(path))
    {
        // 如果传入的字符串不包含具体的路径,程序会默认以默认为当前程序路径 + sfasdfasd,创建文件夹, 
        // 文件夹的确是成功创建了
        DirectoryInfo fDirectoryInfo = Directory.CreateDirectory(path);
        Console.WriteLine(fDirectoryInfo.FullName);
    }

    知识改变命运,奋斗成就人生!
    2009年8月5日 7:48
    版主
  • 我也考虑过使用正则表达式,但是也有个无法解决的问题,比如当PATH为"Z:\20090805"的时候,路径的规则是没有问题,可是没有Z的盘符肯定也是无法创建的,指定固定的盘符就不太灵活,也许实在没有好的解决办法只能这样,现在也是遇到这个问题看看大家有什么好的解决方法.
    为什么下面的代码会产生这样的结果呢?

    zipName = "sfasdfasd";  
              if (!Directory.Exists(zipName))
                {
                    Directory.CreateDirectory(zipName);
                    if (!Directory.Exists(zipName))
                    {
                        return;
                    }
                }
                
            上述代码DEBUG验证了一下,第一个Directory.Exists验证sfasdfasd的PATH不存在,然后CreateDirectory了,下面再次验证时候却返回认为sfasdfasd的PATH是存在的了??实际根本就没有生成文件夹.这是什么原因?
    2009年8月5日 7:54
  • 我也发现这个问题了!这个路径实际上已经被创建了,就在你的应用程序的exe文件的同一个目录下(也就是你没有指定C:\...这样的的绝对路径,就在相对路径下创建),CreateDicroctory方法内部是调用这个方法来实现的,从中你可以看到事实的确如此:
            [ResourceExposure(ResourceScope.Machine)]
            [ResourceConsumption(ResourceScope.Machine)]
            internal unsafe static void InternalCreateDirectory(String fullPath, String path
    #if !FEATURE_PAL
                , DirectorySecurity dirSecurity
    #endif
            )
            {
                int length = fullPath.Length;
     
                // We need to trim the trailing slash or the code will try to create 2 directories of the same name.
                if (length >= 2 && Path.IsDirectorySeparator(fullPath[length - 1]))
                    length--;

                int lengthRoot = Path.GetRootLength(fullPath);

    #if !PLATFORM_UNIX
                // For UNC paths that are only // or ///
                if (length == 2 && Path.IsDirectorySeparator(fullPath[1]))
                    throw new IOException(String.Format(CultureInfo.CurrentCulture, Environment.GetResourceString("IO.IO_CannotCreateDirectory"), path));
    #endif // !PLATFORM_UNIX
     
                List<string> stackDir = new List<string>();

                // Attempt to figure out which directories don't exist, and only
                // create the ones we need.  Note that InternalExists may fail due
                // to Win32 ACL's preventing us from seeing a directory, and this
                // isn't threadsafe.
     
                bool somepathexists = false;
     
                if (length > lengthRoot) { // Special case root (fullpath = X:\\)
                    int i = length-1;
                    while (i >= lengthRoot) {
                        String dir = fullPath.Substring(0, i+1);

                        if (!InternalExists(dir)) // Create only the ones missing
                            stackDir.Add(dir);
                        else
                            somepathexists = true;

                        while (i > lengthRoot && fullPath[i] != Path.DirectorySeparatorChar && fullPath[i] != Path.AltDirectorySeparatorChar) i--;
                        i--;
                    }
                }
     
                int count = stackDir.Count;

                if (stackDir.Count != 0)
                {
                    String [] securityList = new String[stackDir.Count];
                    stackDir.CopyTo(securityList, 0);
                    for (int j = 0 ; j < securityList.Length; j++)
                        securityList[j] += "\\."; // leaf will never have a slash at the end
     
                    // Security check for all directories not present only.
    #if !FEATURE_PAL
                    AccessControlActions control = (dirSecurity == null) ? AccessControlActions.None : AccessControlActions.Change;
                    new FileIOPermission(FileIOPermissionAccess.Write, control, securityList, false, false ).Demand();
    #else
                    new FileIOPermission(FileIOPermissionAccess.Write, securityList, false, false ).Demand();
    #endif
                }
     
                // If we were passed a DirectorySecurity, convert it to a security
                // descriptor and set it in he call to CreateDirectory.
                Win32Native.SECURITY_ATTRIBUTES secAttrs = null;
    #if !FEATURE_PAL
                if (dirSecurity != null) {
                    secAttrs = new Win32Native.SECURITY_ATTRIBUTES();
                    secAttrs.nLength = (int)Marshal.SizeOf(secAttrs);

                    // For ACL's, get the security descriptor from the FileSecurity.
                    byte[] sd = dirSecurity.GetSecurityDescriptorBinaryForm();
                    byte * bytesOnStack = stackalloc byte[sd.Length];
                    Buffer.memcpy(sd, 0, bytesOnStack, 0, sd.Length);
                    secAttrs.pSecurityDescriptor = bytesOnStack;
                }
    #endif
     
                bool r = true;
                int firstError = 0;
                String errorString = path;
                // If all the security checks succeeded create all the directories
                while (stackDir.Count > 0) {
                    String name = stackDir[stackDir.Count - 1];
                    stackDir.RemoveAt(stackDir.Count - 1);
                    if (name.Length > Path.MAX_DIRECTORY_PATH)
                        throw new PathTooLongException(Environment.GetResourceString("IO.PathTooLong"));
                    r = Win32Native.CreateDirectory(name, secAttrs);
                    if (!r && (firstError == 0)) {
                        int currentError = Marshal.GetLastWin32Error();
                        // While we tried to avoid creating directories that don't
                        // exist above, there are at least two cases that will
                        // cause us to see ERROR_ALREADY_EXISTS here.  InternalExists
                        // can fail because we didn't have permission to the
                        // directory.  Secondly, another thread or process could
                        // create the directory between the time we check and the
                        // time we try using the directory.  Thirdly, it could
                        // fail because the target does exist, but is a file.
                        if (currentError != Win32Native.ERROR_ALREADY_EXISTS)
                            firstError = currentError;
                        else {
                            // If there's a file in this directory's place, throw.
                            if (File.InternalExists(name)) {
                                firstError = currentError;
                                // Give the user a nice error message, but don't leak path information.
                                try {
                                    new FileIOPermission(FileIOPermissionAccess.PathDiscovery, GetDemandDir(name, true)).Demand();
                                    errorString = name;
                                }
                                catch(SecurityException) {}
                            }
                        }
                    }
                }

                // We need this check to mask OS differences
                // Handle CreateDirectory("X:\\foo") when X: doesn't exist. Similarly for n/w paths.
                if ((count == 0) && !somepathexists) {
                    String root = InternalGetDirectoryRoot(fullPath);
                    if (!InternalExists(root)) {
                        // Extract the root from the passed in path again for security.
                        __Error.WinIOError(Win32Native.ERROR_PATH_NOT_FOUND, InternalGetDirectoryRoot(path));
                    }
                    return;
                }
     
                // Only throw an exception if creating the exact directory we
                // wanted failed to work correctly.
                if (!r && (firstError != 0)) {
                    __Error.WinIOError(firstError, errorString);
                }
            }

    周雪峰
    • 已标记为答案 寒武纪 2009年8月6日 1:50
    2009年8月5日 8:10
    版主
  • 谢谢XXY,周雪峰的回复,经过你们的提示我在Debug目录下找到了生成的文件夹,原来默认是创建在这里.
    2009年8月6日 1:49
  • 不客气啊!
    欢迎常常来这里和大家交流啊!
    周雪峰
    2009年8月6日 4:47
    版主