Загрузка больших файлов (исключение WebException: соединение было неожиданно закрыто) - PullRequest
6 голосов
/ 30 июня 2009

ОБНОВЛЕНО

См. Сообщение # 3 ниже.

Необходимо загрузить файл в Интернет автоматически (без браузера). Host - Mini File Host 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--

Здесь мы видим параметры, заголовки, тип содержимого и порции информации (1 - имя и тип файла, 2 - содержимое файла, 3 - дополнительные параметры - описание и пароль, не обязательно применяются). Итак, я создал класс, который шаг за шагом эмулирует такое поведение: HttpWebRequest для URL, применяйте необходимые параметры для запроса, формируйте строки запроса с помощью StringBuilder и конвертируйте их в байтовые массивы, читайте файл с помощью FileStream, помещая все это MemoryStream, а затем записывает его в запрос (взял большую часть кода из статьи в CodeProject, где он загружает файл на хост Rapidshare). Опрятно и аккуратно, но ... Кажется, это не работает :(. В результате он возвращает начальную страницу загрузки, а не страницу результатов со ссылками, которые я мог бы проанализировать и представить пользователю ... Вот основные методы класса Uploader:

// 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;
}

Воспроизведение с ProtocolVersion (1.0, 1.1), AllowAutoRedirect (true / false), даже известным ServicePoint.Expect100Continue (false) не устранило проблему. Даже 5-секундный таймаут до получения ответа (хотя в случае большого файла он загружается не так быстро) не помог. Тип контента «octet-stream» был выбран по назначению для загрузки любого файла (можно использовать некоторые переключатели для самых популярных jpg / zip / rar / doc и т. Д., Но этот кажется универсальным). Граница генерируется случайным образом из таймеров, не имеет большого значения. Что-то еще? : / Я мог бы сдаться и забыть об этом, но я чувствую, что довольно близок к решению, и тогда забуду об этом: P. Если вам нужно запустить и отладить целое приложение - вот оно (70 КБ, zip-решение C # 2.0 VS2k8, без рекламы, без вирусов):

@ Mediafire @ FileQube @ FileDropper

Ответы [ 4 ]

10 голосов
/ 01 июля 2009

Обновление: нет, перенаправления нет.

Скриншот

Чтение RFC2388 несколько раз, переписывал код, и он наконец заработал (я думаю, проблема заключалась в том, что конечная граница чтения utf вместо правильной 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) 

Я знаю, что это ошибка в стеке .net, и существует несколько решений:

1) увеличить как Timeout, так и ReadWriteTimeout запроса

2) назначить request.KeepAlive = false и System.Net.ServicePointManager.Expect100Continue = false

3) установить ProtocolVersion на 1,0 Но ни один из них, ни все они в целом не помогают в моем случае. Есть идеи?

РЕДАКТИРОВАТЬ - Исходный код:

// .. 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;
}
0 голосов
/ 21 ноября 2015

В моем случае дублирующееся имя файла также вызывает проблему. Я сохраняю настройки файла в 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>

И, , в другом случае у меня было , проблема в длине файла . Пожалуйста, проверьте допустимый размер файла на вашем сервере. В вашем скрипте просто проверьте эту часть:

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

А если вы не знаете, просто ограничьте размер загружаемого файла в разделе внешнего интерфейса, т.е. HTML <input type="file"> элемент загрузки, это хорошая ссылка для ограничения размера файла и других фильтров .

0 голосов
/ 25 февраля 2010

Попробуйте установить свойство maxRequestLength элемента httpRuntime в файле Web.config.

0 голосов
/ 30 июня 2009

"В результате возвращается начальная страница загрузки, а не страница результатов со ссылками, которые я мог бы проанализировать и представить пользователю ..."

Может быть, это просто поведение функции загрузки: после загрузки вы можете загрузить другой файл? Я думаю, что вы должны вызвать другой URL для страницы "поиск файла" (я полагаю, это то, что вам нужно).


Редактировать : На самом деле, если сервер отправляет «перенаправление» (http 3xx), браузер должен с этим справиться, так что если вы работаете с собственным клиентским приложением вместо браузера , вам придется реализовать перенаправление самостоятельно. Здесь rfc для получения дополнительной информации.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...