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
相关产品推荐
相关产品推荐

