You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

FTP响应流复用报错:Stream was not readable原因排查请求

问题原因与解决办法

嘿,我来帮你捋清楚这个报错的核心原因~

你遇到的“Stream was not readable”错误,不是因为响应流被释放了,而是因为你第一次用responseStream.CopyTo(output)之后,流的位置指针已经移到了末尾,而且FtpWebResponse返回的响应流是**不支持回溯(Seek)**的,没法把指针移回开头重新读取内容。

具体拆解一下:

  • 当你调用Stream.CopyTo()时,它会从当前流的位置开始,一直读到流的末尾,把内容复制到目标流里。执行完这一步,responseStream.Position的值已经等于流的总长度了。
  • 你可以打印responseStream.CanSeek看看,结果肯定是false——这是因为FTP的响应流是一个单向的网络流,只能从头到尾读一次,不能回头重新读。所以哪怕流还在作用域内没被释放,你也没法从末尾位置再读到任何内容,这就触发了报错。

解决办法推荐:

最高效的方式是先把FTP响应流的内容缓存到内存里(用MemoryStream),然后再从内存流里分别写入两个文件:

static void Main(string[] args) {
    // Make sure it is ftp
    if (Properties.Settings.Default.FtpEndpoint.Split(':')[0] != Uri.UriSchemeFtp)
        return;
    
    // Initialize object to communicate to the ftp server
    FtpWebRequest request = (FtpWebRequest)WebRequest.Create(Properties.Settings.Default.FtpEndpoint + "/test.html");
    request.Credentials = new NetworkCredential(Properties.Settings.Default.FtpUser, Properties.Settings.Default.FtpPassword);
    request.Method = WebRequestMethods.Ftp.DownloadFile;
    
    using (FtpWebResponse response = (FtpWebResponse)request.GetResponse())
    using (Stream responseStream = response.GetResponseStream())
    // 先把所有内容缓存到内存流
    using (MemoryStream ms = new MemoryStream())
    {
        responseStream.CopyTo(ms);
        Console.WriteLine("Successfully cached stream to memory");

        // 写入test1.html:先把内存流指针移回开头
        ms.Position = 0;
        using (Stream output = File.OpenWrite(@"C:\Sandbox\vs_projects\FTP\FTP_Download\test1.html"))
        {
            ms.CopyTo(output);
            Console.WriteLine("Successfully wrote stream to test1.html");
        }

        // 写入test2.html:再次把指针移回开头
        ms.Position = 0;
        using (StreamReader reader = new StreamReader(ms))
        {
            string file = reader.ReadToEnd();
            File.WriteAllText(@"C:\Sandbox\vs_projects\FTP\FTP_Download\test2.html", file);
            Console.WriteLine("Successfully wrote stream to test2.html");
        }
    }
}

备选方案(不推荐,仅作参考):

如果文件特别大,内存放不下,你可以发起两次独立的FTP请求,分别下载内容到两个文件。不过这种方式会多一次网络请求,效率较低:

static void Main(string[] args) {
    if (Properties.Settings.Default.FtpEndpoint.Split(':')[0] != Uri.UriSchemeFtp)
        return;

    string ftpUrl = Properties.Settings.Default.FtpEndpoint + "/test.html";
    NetworkCredential credentials = new NetworkCredential(Properties.Settings.Default.FtpUser, Properties.Settings.Default.FtpPassword);

    // 第一次请求:写入test1.html
    using (FtpWebRequest request1 = (FtpWebRequest)WebRequest.Create(ftpUrl))
    {
        request1.Credentials = credentials;
        request1.Method = WebRequestMethods.Ftp.DownloadFile;
        using (FtpWebResponse response1 = (FtpWebResponse)request1.GetResponse())
        using (Stream stream1 = response1.GetResponseStream())
        using (Stream output = File.OpenWrite(@"C:\Sandbox\vs_projects\FTP\FTP_Download\test1.html"))
        {
            stream1.CopyTo(output);
            Console.WriteLine("Successfully wrote stream to test1.html");
        }
    }

    // 第二次请求:写入test2.html
    using (FtpWebRequest request2 = (FtpWebRequest)WebRequest.Create(ftpUrl))
    {
        request2.Credentials = credentials;
        request2.Method = WebRequestMethods.Ftp.DownloadFile;
        using (FtpWebResponse response2 = (FtpWebResponse)request2.GetResponse())
        using (Stream stream2 = response2.GetResponseStream())
        using (StreamReader reader = new StreamReader(stream2))
        {
            string file = reader.ReadToEnd();
            File.WriteAllText(@"C:\Sandbox\vs_projects\FTP\FTP_Download\test2.html", file);
            Console.WriteLine("Successfully wrote stream to test2.html");
        }
    }
}

内容的提问来源于stack exchange,提问作者Jason

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.15 04:47:34