C# → Отправка multipart/form-data данных

Янв 22, 2011


Для меня было большим разочарованием узнать, что стандартный класс WebRequest (HttpWebRequest) не поддерживает отправку данных множественного содержимого. Конечно, в классе WebClient есть метод загрузки файла на удаленный узел, который как раз использует multipart, но он не поможет если кроме файла нужно передать еще и другие данные.

Например, при отправке данных на сервис распознавания каптчи, кроме файла с картинкой, нужно еще загрузить уникальный ключ для идентификации на сервисе.

Сначала я хотел создать класс, который наследовал бы HttpWebRequest, но как оказалось это не возможно. Поэтому пришлось создавать отдельный класс - WebTools.MultiPartForm.

Пример использования

    // using WebTools;
    HttpWebRequest webRequest = (HttpWebRequest)WebRequest.Create("http://antigate.com/in.php");
    using (MultiPartForm multiPart = new MultiPartForm(webRequest))
    {
        multiPart.AddData("method", "post");
        multiPart.AddData("key", "e19567160d1dd900b436978ef3c0c060");
        multiPart.AddFile("file", @"C:\captcha.jpg");
    }
    HttpWebResponse webResponse = (HttpWebResponse)webRequest.GetResponse().Close();

Здесь можно использовать директиву using, так как класс наследует интерфейс IDisposable. При создании экземпляра класса MultiPartForm, в конструктор передается объект WebRequest (HttpWebRequest). После чего происходит добавление строковых данных и содержимого файлов.

Конструктор MultiPartForm

Создает экземпляр класса MultiPartForm.

new MultiPartForm(WebRequest Request)

В качестве аргумента передается объект ранее созданного WebRequest или HttpWebRequest.

Метод AddData

Добавляет в запрос секцию с указанным значением.

void AddData(string Name, string Value)

Метод добавляет секцию Name, значением которой является Value.

Метод AddFile

Добавляет в запрос секцию с содержимым указанного файла.

void AddFile(string Name, string FilePath)

Метод добавляет секцию Name, значением которой является содержимое файла FilePath, с типом данных application/octet-stream.

void AddFile(string Name, string FilePath, string FileType)

Метод добавляет секцию Name, значением которой является содержимое файла FilePath, с типом данных FileType.

void AddFile(string Name, string FilePath, Stream FileStream)

Метод добавляет секцию Name, значением которой является поток данных FileStream, с типом данных application/octet-stream.

void AddFile(string Name, string FilePath, Stream FileStream, string FileType)

Метод добавляет секцию Name, значением которой является поток данных FileStream, с типом данных FileType.

Код класса MultiPartForm

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Net;
using System.IO;
using System.Security.Cryptography;

namespace WebTools
{
    class MultiPartForm : IDisposable
    {
        private Stream _stream;
        private string _boundary;
        private string _templateData = "--{0}\r\nContent-Disposition: form-data; name=\"{1}\"\r\n\r\n{2}\r\n";
        private string _templateFile = "--{0}\r\nContent-Disposition: form-data; name=\"{1}\"; filename=\"{2}\"\r\nContent-Type: {3}\r\n\r\n";
        private string _templateEnd = "--{0}--\r\n\r\n";

        public MultiPartForm(WebRequest Request)
        {
            _boundary = String.Format("--{0}", GetMD5());
            Request.Method = "POST";
            Request.ContentType = String.Format("multipart/form-data; boundary={0}", _boundary);
            _stream = Request.GetRequestStream();
        }

        public void AddData(string Name, string Value)
        {
            byte[] contentData = Encoding.UTF8.GetBytes(String.Format(_templateData, _boundary, Name, Value));
            _stream.Write(contentData, 0, contentData.Length);
        }

        public void AddFile(string Name, string FilePath)
        {
            AddFile(Name, FilePath, "application/octet-stream");
        }

        public void AddFile(string Name, string FilePath, string FileType)
        {
            using (FileStream fileStream = new FileStream(FilePath, FileMode.Open))
            {
                AddFile(Name, FilePath, fileStream, FileType);
            }
        }

        public void AddFile(string Name, string FilePath, Stream FileStream)
        {
            AddFile(Name, FilePath, FileStream, "application/octet-stream");
        }

        public void AddFile(string Name, string FilePath, Stream FileStream, string FileType)
        {
            FileStream.Seek(0, SeekOrigin.Begin);
            byte[] contentFile = Encoding.UTF8.GetBytes(String.Format(_templateFile, _boundary, Name, FilePath, FileType));
            _stream.Write(contentFile, 0, contentFile.Length);
            FileStream.CopyTo(_stream);
            byte[] _lineFeed = Encoding.UTF8.GetBytes("\r\n");
            _stream.Write(_lineFeed, 0, _lineFeed.Length);
        }

        public void Dispose()
        {
            Close();
            GC.SuppressFinalize(this);
        }

        public void Close()
        {
            byte[] contentEnd = Encoding.UTF8.GetBytes(String.Format(_templateEnd, _boundary));
            _stream.Write(contentEnd, 0, contentEnd.Length);
        }

        private string GetMD5()
        {
            Random randNum = new Random();
            MD5CryptoServiceProvider md5hash = new MD5CryptoServiceProvider();
            byte[] randByte = Encoding.UTF8.GetBytes(randNum.NextDouble().ToString());
            byte[] computeHash = md5hash.ComputeHash(randByte);
            string resultHash = String.Empty;
            foreach (byte currentByte in computeHash)
            {
                resultHash += currentByte.ToString("x2");
            }
            return resultHash;
        }
    }
}

UPDATE:
Убрал метод получения MIME типа, который использовал стороннюю библиотеку. Теперь MIME можно указать при вызове метода AddFile.

        [DllImport("urlmon.dll", CharSet = CharSet.Auto)]
        private static extern int FindMimeFromData(IntPtr ptr, string url, byte[] buffer, int bufferSize,
            string mimeProposed, int mimeFlags, out string mimeType, int dwReserved);

        private string GetMimeType(Stream fileStream)
        {
            string resultMime;
            byte[] fileBytes = new byte[fileStream.Length];
            fileStream.Read(fileBytes, 0, (int)fileStream.Length);
            FindMimeFromData(new IntPtr(), null, fileBytes, fileBytes.Length, null, 0, out resultMime, 0);
            return resultMime;
        }

Скачать webtools.multipartform.zip
Размер: 1 kB – Скачиваний: 103 – Скачан последний раз: 14.08.2012 00:46

Post to Twitter

Похожие статьи:

  1. Создание базы данных SQLite
  2. Класс распознавания для antigate.com
  3. Генерация MD5 хэша
  4. Критика класса LiveInternet
  5. Сходства и различия Delphi vs C#

Комментарии (10)

  1. avatar

    Eugene
    Январь 22nd, 2011 at 13:41 #

    Хороший пример

  2. avatar

    Ghostonline
    Март 25th, 2011 at 15:01 #

    ОГРОМНЫЙ минус этого класса в том что он заточен под WebRequest
    А что если ты начнешь использовать нормальную сетевую библиотеку? Придется модифицировать класс.
    Учитесь абстрагироваться уже.

  3. avatar

    Серёга
    Май 24th, 2011 at 23:42 #

    Ghostonline, поясни, что ты имеешь в виду. В чём проблема WebRequest, зачем абстрагироваться и как это лучше сделать?

  4. avatar

    Radzhab
    Сентябрь 18th, 2011 at 10:14 #

    TC) может ответишь в аське*?

  5. avatar

    Radzhab
    Сентябрь 24th, 2011 at 10:25 #

    При отправке в запросе русских слов выходят крякозябры. Например :
    req.AddData(«post»,»Отправить»).
    Вместо «Отправить» хрень какая то выходит

    И еще- Как отправить кукисы ? Например у меня в запросе три кукиса. Как их отправить ????

  6. avatar

    GlooK
    Сентябрь 24th, 2011 at 10:39 #

    По поводу кракозябр, нужно с кодировкой поиграться.
    Попробуй кукисы вставить так:

    CookieContainer container = new CookieContainer();
    HttpWebRequest myWebRequest = (HttpWebRequest)WebRequest.Create(url);
    myWebRequest.CookieContainer = container;
    container.Add(new Cookie(«username», «glook», «/», «.google.com»));
    using (MultiPartForm multiPart = new MultiPartForm(myWebRequest))
    {
    multiPart.AddData(«method», «post»);
    multiPart.AddData(«key», «e19567160d1dd900b436978ef3c0c060″);
    multiPart.AddFile(«file», @»C:\captcha.jpg»);
    }

  7. avatar

    Radzhab
    Сентябрь 24th, 2011 at 22:47 #

    а где именно настраивается кодировка httprequest ?

  8. avatar

    Алексей
    Ноябрь 26th, 2011 at 09:54 #

    Это под какой версией .Net работает? 1, 2, 3 ???

  9. avatar

    Роман
    Январь 20th, 2012 at 08:51 #

    Еее, товарищ — спасибо, ты лучший! Три дня бился с этим, а щас всё заработало! Аригато!

  10. avatar

    sabi
    Февраль 5th, 2012 at 01:40 #

    Я в этом деле новичок, поэтому не подскажете, что делать с *.cs файлом?

Ваш комментарий

Rambler's Top100 Яндекс.Метрика