在C#中从URI字符串获取文件名

239

我有一个从字符串URI中获取文件名的方法。有什么办法可以使它更加健壮?

private string GetFileName(string hrefLink)
{
    string[] parts = hrefLink.Split('/');
    string fileName = "";

    if (parts.Length > 0)
        fileName = parts[parts.Length - 1];
    else
        fileName = hrefLink;

    return fileName;
}
10个回答

448
你可以创建一个 System.Uri 对象,使用 IsFile 来验证它是否是文件,然后使用 Uri.LocalPath 提取文件名。
这样做更安全,因为它提供了一种检查 URI 有效性的方法。

回应评论而进行编辑:

要仅获取完整的文件名,我会使用:

Uri uri = new Uri(hreflink);
if (uri.IsFile) {
    string filename = System.IO.Path.GetFileName(uri.LocalPath);
}

这将为您完成所有的错误检查,而且是平台无关的。所有特殊情况都会迅速轻松地处理好。

2
没错,但我只需要文件名,而不是完整的文件路径。那么我还需要在Uri.LocalPath上执行这一步骤吗? - paulwhit
3
在这种情况下,您应该在Uri.LocalPath的结果上使用Path.GetFileName。这是一种完全安全、经过高度检查的处理方式。我将编辑我的答案以包括这一点。请参阅:http://msdn.microsoft.com/en-us/library/system.io.path.getfilename.aspx。 - Reed Copsey
55
isFile 函数似乎只检查文件路径的协议。因此,“http://www/myFile.jpg” 返回 false,“file://www/something.jpg” 返回 true,所以在这种情况下它是无用的。 - dethSwatch
值得注意的是,Uri 可以包含在文件系统路径上无法存在的字符,例如 " 和 <。Path 类会检查这些无效字符,并在存在时引发异常。使用 Uri.LocalPath 不会删除这些字符。我在 这篇文章 中详细介绍了这个问题。 - Kam Figy
10
还要注意查询字符串。http://www.test.com/file1.txt?a=b 将导致 file1.txt?a=b - Julian
显示剩余8条评论

91

Uri.IsFile无法处理http URL,它只适用于"file://"。根据MSDN:当Scheme属性等于UriSchemeFile时,IsFile属性为true。因此你不能依赖它。

Uri uri = new Uri(hreflink);
string filename = System.IO.Path.GetFileName(uri.LocalPath);

Uri.LocalPath 进行了 Windows 特定的转换,在非 Windows 环境下无法正常工作。请参见下面的答案,以了解一种可移植的方法来解决这个问题。 - Kostub Deshmukh
虽然您无法使用 Uri.IsFile 来测试 http URL/协议,但是您可以成功地从 http URL 中提取文件名,方法是使用 System.IO.Path.GetFileName(url); - Alex P.

77

大多数其他答案要么不完整,要么没有处理路径后面的内容(查询字符串/哈希)。

readonly static Uri SomeBaseUri = new Uri("http://canbeanything");

static string GetFileNameFromUrl(string url)
{
    Uri uri;
    if (!Uri.TryCreate(url, UriKind.Absolute, out uri))
        uri = new Uri(SomeBaseUri, url);

    return Path.GetFileName(uri.LocalPath);
}

测试结果:

GetFileNameFromUrl("");                                         // ""
GetFileNameFromUrl("test");                                     // "test"
GetFileNameFromUrl("test.xml");                                 // "test.xml"
GetFileNameFromUrl("/test.xml");                                // "test.xml"
GetFileNameFromUrl("/test.xml?q=1");                            // "test.xml"
GetFileNameFromUrl("/test.xml?q=1&x=3");                        // "test.xml"
GetFileNameFromUrl("test.xml?q=1&x=3");                         // "test.xml"
GetFileNameFromUrl("http://www.a.com/test.xml?q=1&x=3");        // "test.xml"
GetFileNameFromUrl("http://www.a.com/test.xml?q=1&x=3#aidjsf"); // "test.xml"
GetFileNameFromUrl("http://www.a.com/a/b/c/d");                 // "d"
GetFileNameFromUrl("http://www.a.com/a/b/c/d/e/");              // ""

8
дёәд»Җд№ҲGetFileNameFromUrl("test")зҡ„з»“жһңжҳҜ"test.xml"пјҹиҝҷжҳҜеҗҰеҸӘжҳҜдёҖдёӘ笔иҜҜпјҹ - ckittel
截至.NET Core 3.0,此功能无法正常工作(查询字符串未从路径中删除)。 - Alexandre Daubricourt
@AlexandreDaubricourt 我刚在 Windows 上测试了 netcore 3.0、3.1 和 net5.0,输出结果都是正确的,没有任何变化。这段代码是否在不同的操作系统下运行 netcore 3.0 时出现了问题? - Ronnie Overby

37

对于 HTTP URL,被接受的答案存在问题。此外,Uri.LocalPath 会进行 Windows 特有的转换,并且正如某人指出的那样,在其中保留查询字符串。更好的方法是使用Uri.AbsolutePath

处理 HTTP URL 的正确方式是:

Uri uri = new Uri(hreflink);
string filename = System.IO.Path.GetFileName(uri.AbsolutePath);

9
请注意,对于转义的 URL(例如 http://example.com/dir/hello%20world.txt),这将返回 hello%20world.txt,而 Uri.LocalPath 方法会返回 hello world.txt - Jeff Moser

26

我认为这将满足您的需求:

var uri = new Uri(hreflink);
var filename = uri.Segments.Last();

4
这确实看起来是一个优雅的解决方案,但要记住它仅适用于绝对URI,并返回已编码/转义的值(使用Uri.UnescapeDataString()将%20和+更改为空格)。 - Ronald

8
using System.IO;

private String GetFileName(String hrefLink)
{
    return Path.GetFileName(hrefLink.Replace("/", "\\"));
}

这当然是在假设你已经解析出文件名的情况下。
编辑 #2:
using System.IO;

private String GetFileName(String hrefLink)
{
    return Path.GetFileName(Uri.UnescapeDataString(hrefLink).Replace("/", "\\"));
}

这应该处理文件名中的空格等字符。


3
冒号在某些平台的路径中是不被接受的,因此这种方法可能在某些 *nix 变体上运行 Mono.NET 时失败。最好使用 System.Uri,因为它专门设计用于满足 OP 的需求。 - richardtallent
1
一个很好的观点!我总是忘记Mono。我考虑了空格之类的东西,但没有考虑到冒号。 - Mike Hofer

6
截至2020年,处理查询字符串&编码的URL。
public static string GetFileNameFromUrl (string url)
{
    var decoded = HttpUtility.UrlDecode(url);

    if (decoded.IndexOf("?") is {} queryIndex && queryIndex != -1)
    {
        decoded = decoded.Substring(0, queryIndex);
    }

    return Path.GetFileName(decoded);
}

2

这是我的示例,你可以使用:

        public static string GetFileNameValidChar(string fileName)
    {
        foreach (var item in System.IO.Path.GetInvalidFileNameChars())
        {
            fileName = fileName.Replace(item.ToString(), "");
        }
        return fileName;
    }

    public static string GetFileNameFromUrl(string url)
    {
        string fileName = "";
        if (Uri.TryCreate(url, UriKind.Absolute, out Uri uri))
        {
            fileName = GetFileNameValidChar(Path.GetFileName(uri.AbsolutePath));
        }
        string ext = "";
        if (!string.IsNullOrEmpty(fileName))
        {
            ext = Path.GetExtension(fileName);
            if (string.IsNullOrEmpty(ext))
                ext = ".html";
            else
                ext = "";
            return GetFileNameValidChar(fileName + ext);

        }

        fileName = Path.GetFileName(url);
        if (string.IsNullOrEmpty(fileName))
        {
            fileName = "noName";
        }
        ext = Path.GetExtension(fileName);
        if (string.IsNullOrEmpty(ext))
            ext = ".html";
        else
            ext = "";
        fileName = fileName + ext;
        if (!fileName.StartsWith("?"))
            fileName = fileName.Split('?').FirstOrDefault();
        fileName = fileName.Split('&').LastOrDefault().Split('=').LastOrDefault();
        return GetFileNameValidChar(fileName);
    }

使用方法:

var fileName = GetFileNameFromUrl("http://cdn.p30download.com/?b=p30dl-software&f=Mozilla.Firefox.v58.0.x86_p30download.com.zip");

1
简单明了:

简单直接:

            Uri uri = new Uri(documentAttachment.DocumentAttachment.PreSignedUrl);
            fileName = Path.GetFileName(uri.LocalPath);

0
  //First Method to get fileName from fileurl  


 List<string> fileNameListValues = new List<string>();  

//fileNameListValues List  consist of fileName and fileUrl 
//we need to get fileName and fileurl from fileNameListValues List 
 name 

 foreach(var items in fileNameListValues)
 {
var fileUrl = items;
var uriPath = new Uri(items).LocalPath;
var fileName = Path.GetFileName(uriPath);
 }


  //Second Way to get filename from fileurl is ->      


 fileNameValue = "https://projectname.com/assets\UploadDocuments\documentFile_637897408013343662.jpg";
 fileName = " documentFile_637897408013343662.jpg";

 //way to get filename from fileurl 

 string filename = 
  fileNameValue.Substring(fileNameValue.LastIndexOf("\\") + 1);

你觉得这样做有什么优势,比起被接受的答案的方式?它是否克服了使用那种方式可能存在的问题?如果没有,那么这种方法改进于那个答案的原因是什么? - shelleybutterfly

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接