C# 大文件上传(WebException:连接意外关闭)

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/1060966/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-06 07:17:02  来源:igfitidea点击:

Big files uploading (WebException: The connection was closed unexpectedly)

c#.netfileuploadhttpwebrequest

提问by Jaded

UPDATED

更新

See post #3below.

请参阅下面的帖子#3

There is a need to upload a file to the web automatically (without browser). Host - Mini File Host v1.2(if this does matter). Didn't find specific api in documentation, so at first i sniffed browser requests in Firebug as follows :

需要自动将文件上传到网络(无需浏览器)。主机 -迷你文件主机 v1.2(如果这很重要)。在文档中没有找到特定的 api,所以起初我在 Firebug 中嗅探浏览器请求如下:

Params : do
Value : verify
POST /upload.php?do=verify HTTP/1.1
Host: webfile.ukrwest.net
User-Agent: Mozilla/5.0 (Windows; U; Windows NT 5.2; ru; rv:1.9.0.8) Gecko/2009032609 Firefox/3.0.8 (.NET CLR 4.0.20506)
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8
Accept-Language: ru,en-us;q=0.7,en;q=0.3
Accept-Encoding: gzip,deflate
Accept-Charset: windows-1251,utf-8;q=0.7,*;q=0.7
Keep-Alive: 300
Connection: keep-alive
Referer: http://filehoster.awardspace.com/index.php
Content-Type: multipart/form-data; boundary=---------------------------27368237179714
Content-Length: 445

-----------------------------27368237179714
Content-Disposition: form-data; name="upfile"; filename="Test.file"
Content-Type: application/octet-stream

12345678901011121314151617sample text
-----------------------------27368237179714
Content-Disposition: form-data; name="descr"


-----------------------------27368237179714
Content-Disposition: form-data; name="pprotect"


-----------------------------27368237179714--

Here we can see parameter, headers, content type and chunks of information (1 - file name and type, 2 - file contents, 3 - additional params - description and password, not necessarily applied). So i've created a class that emulates such a behaviour step by step : HttpWebRequest on the url, apply needed parameters to request, form request strings with StringBuilder and convert them to byte arrays, read a file using FileStream, putting all that stuff to MemoryStream and then writing it to request (took major part of a code from an article at CodeProject where it uploads a file to Rapidshare host). Neat and tidy, but... It doesn't seem to work :(. As result it returns initial upload page, not a result page with links i could parse and present to a user... Here are main methods of an Uploader class :

在这里我们可以看到参数、标题、内容类型和信息块(1 - 文件名和类型,2 - 文件内容,3 - 附加参数 - 描述和密码,不一定适用)。因此,我创建了一个逐步模拟这种行为的类:在 url 上使用 HttpWebRequest,将所需参数应用于请求,使用 StringBuilder 形成请求字符串并将它们转换为字节数组,使用 FileStream 读取文件,将所有这些内容放入MemoryStream 然后将其写入请求(从 CodeProject 的一篇文章中获取大部分代码,其中将文件上传到 Rapidshare 主机)。整洁,但是......它似乎不起作用:(。结果它返回初始上传页面,而不是带有链接的结果页面,我可以解析并呈现给用户......这里是上传器的主要方法班级 :

// Step 1 - request creation 
 private HttpWebRequest GetWebrequest(string boundary)
 {
            Uri uri = new Uri("http://filehoster.awardspace.com/index.php?do=verify");
            System.Net.HttpWebRequest httpWebRequest = (System.Net.HttpWebRequest)System.Net.WebRequest.Create(uri);
            httpWebRequest.CookieContainer = _cookies;
            httpWebRequest.ContentType = "multipart/form-data; boundary=" + boundary;
            httpWebRequest.UserAgent = "Mozilla/5.0 (Windows; U; Windows NT 5.2; ru; rv:1.9.0.8) Gecko/2009032609 Firefox/3.0.8 (.NET CLR 4.0.20506)";
            httpWebRequest.Referer = "http://filehoster.awardspace.com/index.php";
            httpWebRequest.Method = "POST";
            httpWebRequest.KeepAlive = true;
            httpWebRequest.Timeout = -1;
            //httpWebRequest.Accept = "text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8";
            httpWebRequest.Headers.Add("Accept-Charset", "windows-1251,utf-8;q=0.7,*;q=0.7");
            httpWebRequest.Headers.Add("Accept-Encoding", "gzip,deflate");
            httpWebRequest.Headers.Add("Accept-Language", "ru,en-us;q=0.7,en;q=0.3");
            //httpWebRequest.AllowAutoRedirect = true;
            //httpWebRequest.ProtocolVersion = new Version(1,1);
            //httpWebRequest.SendChunked = true;
            //httpWebRequest.Headers.Add("Cache-Control", "no-cache");
            //httpWebRequest.ServicePoint.Expect100Continue = false;
            return httpWebRequest;
}
// Step 2 - first message part (before file contents)
private string GetRequestMessage(string boundary, string FName, string description, string password)
{    
            System.Text.StringBuilder stringBuilder = new StringBuilder();
            stringBuilder.Append("--");
            stringBuilder.Append(boundary);
            stringBuilder.Append("\r\n");
            stringBuilder.Append("Content-Disposition: form-data; name=\"");
            stringBuilder.Append("upfile");
            stringBuilder.Append("\"; filename=\"");
            stringBuilder.Append(FName);
            stringBuilder.Append("\"");
            stringBuilder.Append("\r\n");
            stringBuilder.Append("Content-Type: application/octet-stream");
            stringBuilder.Append("\r\n");
            return stringBuilder.ToString();
}
// Step 4 - additional request parameters. Step 3 - reading file is in method below
private string GetRequestMessageEnd(string boundary)
{    
            System.Text.StringBuilder stringBuilder = new StringBuilder();
            stringBuilder.Append(boundary);
            stringBuilder.Append("\r\n");
            stringBuilder.Append("Content-Disposition: form-data; name=\"descr\"");
            stringBuilder.Append("\r\n");
            stringBuilder.Append("\r\n");
            stringBuilder.Append("Default description");
            stringBuilder.Append("\r\n");
            stringBuilder.Append(boundary);
            stringBuilder.Append("\r\n");
            stringBuilder.Append("Content-Disposition: form-data; name=\"pprotect\"");
            stringBuilder.Append("\r\n");
            stringBuilder.Append("\r\n");
            stringBuilder.Append("");
            stringBuilder.Append("\r\n");
            stringBuilder.Append(boundary);
            stringBuilder.Append("--");
            //stringBuilder.Append("\r\n");
            //stringBuilder.Append(boundary);
            //stringBuilder.Append("\r\n");
            return stringBuilder.ToString();
}
// Main method
public string ProcessUpload(string FilePath, string description, string password)
{
            // Chosen file information
            FileSystemInfo _file = new FileInfo(FilePath);
            // Random boundary generation
            DateTime dateTime2 = DateTime.Now;
            long l2 = dateTime2.Ticks;
            string _generatedBoundary = "----------" + l2.ToString("x");
            // Web request creation
            System.Net.HttpWebRequest httpWebRequest = GetWebrequest(_generatedBoundary);
            // Main app block - form and send request
            using (System.IO.FileStream fileStream = new FileStream(_file.FullName, FileMode.Open, FileAccess.Read, FileShare.Read))
            {
                byte[] bArr1 = Encoding.ASCII.GetBytes("\r\n--" + _generatedBoundary + "\r\n");
                // Generating pre-content post message
                string firstPostMessagePart = GetRequestMessage(_generatedBoundary, _file.Name, description, password);
                // Writing first part of request
                byte[] bArr2 = Encoding.UTF8.GetBytes(firstPostMessagePart);
                Stream memStream = new MemoryStream();
                memStream.Write(bArr1, 0, bArr1.Length);
                memStream.Write(bArr2, 0, bArr2.Length);
                // Writing file
                byte[] buffer = new byte[1024];
                int bytesRead = 0;
                while ((bytesRead = fileStream.Read(buffer, 0, buffer.Length)) != 0)
                {
                    memStream.Write(buffer, 0, bytesRead);
                }
                // Generating end of a post message
                string secondPostMessagePart = GetRequestMessageEnd(_generatedBoundary);
                byte[] bArr3 = Encoding.UTF8.GetBytes(secondPostMessagePart);
                memStream.Write(bArr3, 0, bArr3.Length);
                // Preparing to send
                httpWebRequest.ContentLength = memStream.Length;
                fileStream.Close();

                Stream requestStream = httpWebRequest.GetRequestStream();

                memStream.Position = 0;
                byte[] tempBuffer = new byte[memStream.Length];
                memStream.Read(tempBuffer, 0, tempBuffer.Length);
                memStream.Close();
                // Sending request
                requestStream.Write(tempBuffer, 0, tempBuffer.Length);
                requestStream.Close();
            }
            // Delay (?)
            System.Threading.Thread.Sleep(5000);
            // Getting response
            string strResponse = "";
            using (Stream stream = httpWebRequest.GetResponse().GetResponseStream())
            using (StreamReader streamReader = new StreamReader(stream/*, Encoding.GetEncoding(1251)*/))
            {
                strResponse = streamReader.ReadToEnd();
            }
            return strResponse;
}

Plays with ProtocolVersion (1.0, 1.1), AllowAutoRedirect (true/false), even known ServicePoint.Expect100Continue (false) didn't fix an issue. Even a 5sec timeout before getting response (thought in case of a big file it doesn't uploads so quick) didn't help. Content type "octet-stream" was chosen by purpose to upload any file (could use some switch for most popular jpg/zip/rar/doc etc., but that one seems universal). Boundary is generated randomly from timer ticks, not a big deal. What else? :/ I could give up and forget this, but i feel i'm pretty close to solve and thenforget about it :P. In case you need the whole application to run and debug - here it is (70kb, zipped C# 2.0 VS2k8 solution, no ads, no viruses) :

使用 ProtocolVersion (1.0, 1.1)、AllowAutoRedirect (true/false),甚至已知的 ServicePoint.Expect100Continue (false) 都没有解决问题。即使在获得响应之前 5 秒超时(认为在大文件的情况下它不会如此快地上传)也无济于事。内容类型“octet-stream”是有意选择来上传任何文件的(可以对最流行的 jpg/zip/rar/doc 等使用一些开关,但那个似乎通用)。边界是从计时器滴答中随机生成的,没什么大不了的。还有什么?:/我可以放弃并忘记这一点,但我觉得我很接近解决然后忘记它:P。如果您需要运行和调试整个应用程序 - 这是(70kb,压缩的 C# 2.0 VS2k8 解决方案,无广告,无病毒):

@Mediafire@FileQube@FileDropper

@Mediafire @FileQube @FileDropper

采纳答案by Jaded

Update : nope, there is no redirect.

更新:不,没有重定向。

screenshot

截屏

Read RFC2388few times, rewrote the code and it finally worked (i guess the trouble was in utf-read trailing boundary instead of correct 7 bit ascii). Hooray? Nope :(. Only small files are transfered, big ones throwing "The connection was closed unexpectedly".

阅读RFC2388几次,重新编写代码,它终于起作用了(我猜问题在于 utf-read 尾随边界而不是正确的 7 位 ascii)。万岁?不:(。只传输小文件,大文件抛出“连接意外关闭”。

System.Net.WebException was unhandled by user code
  Message="The underlying connection was closed: The connection was closed unexpectedly."
  Source="Uploader"
  StackTrace:
   at Uploader.Upload.ProcessUpload(String FilePath, String description, String password) in F:\MyDocuments\Visual Studio 2008\Projects\Uploader\Uploader.cs:line 96
   at Uploader.Form1.backgroundWorker1_DoWork(Object sender, DoWorkEventArgs e) in F:\MyDocuments\Visual Studio 2008\Projects\Uploader\Form1.cs:line 45
   at System.ComponentModel.BackgroundWorker.WorkerThreadStart(Object argument) 

I know that's a bug with .net stack and few solutions exists :

我知道这是 .net 堆栈的一个错误,并且几乎没有解决方案:

1) increase both Timeout and ReadWriteTimeout of request

1) 增加请求的 Timeout 和 ReadWriteTimeout

2) assign request.KeepAlive = false and System.Net.ServicePointManager.Expect100Continue = false

2)分配 request.KeepAlive = false 和 System.Net.ServicePointManager.Expect100Continue = false

3) set ProtocolVersion to 1.0 But neither one of them nor all of them altogether help in my case. Any ideas?

3) 将 ProtocolVersion 设置为 1.0 但在我的情况下,它们中的任何一个或全部都没有帮助。有任何想法吗?

EDIT- Source code:

编辑- 源代码:

// .. request created, required params applied
httpWebRequest.ProtocolVersion = HttpVersion.Version10; // fix 1
httpWebRequest.KeepAlive = false; // fix 2
httpWebRequest.Timeout = 1000000000; // fix 3
httpWebRequest.ReadWriteTimeout = 1000000000; // fix 4
// .. request processed, data written to request stream
string strResponse = "";            
try
{
    using (WebResponse httpResponse = httpWebRequest.GetResponse()) // error here
        {
            using (Stream responseStream = httpResponse.GetResponseStream())
            {
                using (StreamReader streamReader = new StreamReader(responseStream))
                    {
                        strResponse = streamReader.ReadToEnd();
                    }
                }
            }
        }
catch (WebException exception)
{
    throw exception;
}

回答by fretje

"As result it returns initial upload page, not a result page with links i could parse and present to a user..."

“结果它返回初始上传页面,而不是带有链接的结果页面,我可以解析并呈现给用户......”

Maybe that's just the behaviour of the upload functionality: that after the upload has finished, you can upload another file? I think you have to call another url for the "browse for file"-page (I suppose that's the one you need).

也许这只是上传功能的行为:上传完成后,您可以上传另一个文件?我认为您必须为“浏览文件”页面调用另一个网址(我想这就是您需要的网址)。



Edit: Actually, if the server sends a "redirect" (http 3xx), that's something the browser has to handle, so if you're working with your own client application in stead of a browser, you'll have to implement redirection yourself. Here the rfcfor more information.

编辑:实际上,如果服务器发送“重定向”(http 3xx),这是浏览器必须处理的事情,因此如果您使用自己的客户端应用程序而不是浏览器,则必须自己实现重定向. 此处为rfc以获取更多信息。

回答by fredmerlo

Try setting the maxRequestLength property of the httpRuntime element in the Web.config.

尝试在 Web.config 中设置 httpRuntime 元素的 maxRequestLength 属性。

回答by Hanny Setiawan

In my case, duplicate filename causing the issue as well. I save the file's settings in an xml file but the name setting is duplicating each other.

就我而言,重复的文件名也会导致问题。我将文件的设置保存在一个 xml 文件中,但名称设置相互重复。

      <field name="StillImage">
        <prefix>isp_main_</prefix>
        <suffix>308</suffix>
        <width>1080</width>
        <height>1080</height>
      </field>
      <field name="ThumbnailImage">
        <prefix>isp_thumb_</prefix> // pay attention to this
        <suffix>308</suffix>
        <width>506</width>
        <height>506</height>
      </field>
      <field name="Logo">
        <prefix>isp_thumb_</prefix> // and this
        <suffix>306</suffix>
        <width>506</width>
        <height>506</height>
      </field>

And, in the other case I had, the issue is in the file length. Please do check the allowed file size on your server. In your script just do check this part :

而且,在我遇到的另一种情况下,问题在于文件长度。请检查您的服务器上允许的文件大小。在您的脚本中只需检查这部分:

dataStream.Write(filesBytesArray, 0, filesBytesArray.Length);
dataStream.Close();

And if you dont know, just limit the file uploaded size in your frontend section ie. HTML <input type="file">upload element, this is good reference for limiting file size and other filter.

如果您不知道,只需限制前端部分中上传的文件大小即可。HTML<input type="file">上传元素,这是限制文件大小和其他过滤器的良好参考