使用 FtpWebRequest 下载文件时获取文件名而不是内容

Getting file name instead of contents when downloading file with FtpWebRequest

我正在尝试编写一个可以从人口普查局的 TIGER FTP 站点下载 shapefile 的工具,该站点位于:ftp://ftp2.census.gov/geo/tiger/TIGER2015/TRACT。我能够很好地列出文件,但是当我尝试下载文件时,我得到的字符串只是文件名,而不是文件内容本身。代码如下。

    private static T FtpRequest<T, TReader>(string url, string method, Func<TReader, T> responseAction) where TReader : IDisposable {
        var request = (FtpWebRequest)WebRequest.Create(url);
        request.Method = WebRequestMethods.Ftp.ListDirectory;
        request.Credentials = new NetworkCredential("anonymous", "anonymous");
        request.UseBinary = true;
        request.UsePassive = true;
        var response = (FtpWebResponse)request.GetResponse();
        using(var responseStream = response.GetResponseStream())
        using(var reader = (TReader)Activator.CreateInstance(typeof(TReader), responseStream)) {
            return responseAction(reader);
        }
    }

    private static IList<string> ListDirectory(string url) {
        return FtpRequest(url,
                          WebRequestMethods.Ftp.ListDirectory,
                          (StreamReader reader) => reader.ReadAllLines()
                                                         .Select(file => new Uri(new Uri(url), new Uri(file, UriKind.Relative)).AbsoluteUri)
                                                         .ToArray());
    }

    private static byte[] DownloadBinaryFile(string url) {
        // NOTE: following code works in 4.6, but not 3.5
        //using(var client = new WebClient()) {
        //  return client.DownloadData(url);
        //}

        // this code returns file name as string in both 4.6 and 3.5
        return FtpRequest(url,
                          WebRequestMethods.Ftp.DownloadFile,
                          (BinaryReader reader) => {
                              using(var memoryStream = new MemoryStream()) {
                                  var buffer = new byte[2048];

                                  for(;;) {
                                      var bytesRead = reader.Read(buffer, 0, buffer.Length);
                                      if(bytesRead == 0)
                                          break;

                                      memoryStream.Write(buffer, 0, bytesRead);
                                  }

                                  memoryStream.Position = 0;
                                  return memoryStream.GetBuffer();
                              }
                          });
    }

    internal static void Main(string[] args) {
        var baseUrl = "ftp://ftp2.census.gov/geo/tiger/TIGER2015/TRACT";

        foreach(var file in ListDirectory(baseUrl)) {
            Console.WriteLine("Downloading: {0}", file);
            var contents = DownloadBinaryFile(file);
            using(var zipStream = new MemoryStream(contents))
            using(var zipFile = ZipFile.Read(zipStream)) {
                foreach(var entry in zipFile) {
                    Console.WriteLine(" -> {0}", entry.FileName);
                    //using(var entryReader = entry.OpenReader()) {

                    //}
                }
            }
        }

        Console.ReadKey();
    }

如果我使用 WebClient,它会在 .NET 3.5 中失败并出现 550 错误,但在 .NET 4.6 中可以正常工作。上面注释掉了相关代码。

人口普查网站有点挑剔,所以他们的网站完全有可能在做奇怪的事情。这就是我提供 URL 的原因,以防任何 FTP 知识比我多的人可以诊断出故障服务器。

在您的 FtpRequest 方法中,您总是使用 WebRequestMethods.Ftp.ListDirectory 方法,即使是下载。因此,您实际上是 "list" 文件,这就是您在响应中获得其名称的原因。

您必须使用 WebRequestMethods.Ftp.DownloadFile method 下载文件。