Составные формы из клиента C#

Я пытаюсь заполнить форму в приложении php из клиента C# (надстройка Outlook). Я использовал Fiddler, чтобы увидеть исходный запрос из приложения php, и форма передается как multipart / form. К сожалению, .Net не имеет встроенной поддержки для этого типа форм (у WebClient есть только метод загрузки файла). Кто-нибудь знает библиотеку или имеет код для этого? Хочу разместить разные значения и дополнительно (но только иногда) файл.

Спасибо за вашу помощь, Себастьян

Работает как шарм www.briangrinstead.com/blog

Chathuranga Wijeratna 26.10.2009 09:26

Если вы не возражаете против небольшой зависимости библиотеки, Flurl делает это настолько простым, насколько это возможно. [Отказ от ответственности: я автор]

Todd Menier 23.06.2017 18:27
Стоит ли изучать PHP в 2026-2027 годах?
Стоит ли изучать PHP в 2026-2027 годах?
Привет всем, сегодня я хочу высказать свои соображения по поводу вопроса, который я уже много раз получал в своем сообществе: "Стоит ли изучать PHP в...
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Приемы CSS-макетирования - floats и Flexbox
Приемы CSS-макетирования - floats и Flexbox
Здравствуйте, друзья-студенты! Готовы совершенствовать свои навыки веб-дизайна? Сегодня в нашем путешествии мы рассмотрим приемы CSS-верстки - в...
Тестирование функциональных ngrx-эффектов в Angular 16 с помощью Jest
В системе управления состояниями ngrx, совместимой с Angular 16, появились функциональные эффекты. Это здорово и делает код определенно легче для...
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
53
2
116 713
10
Перейти к ответу Данный вопрос помечен как решенный

Ответы 10

Ответ принят как подходящий

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

public class PostData
{

    private List<PostDataParam> m_Params;

    public List<PostDataParam> Params
    {
        get { return m_Params; }
        set { m_Params = value; }
    }

    public PostData()
    {
        m_Params = new List<PostDataParam>();

        // Add sample param
        m_Params.Add(new PostDataParam("email", "MyEmail", PostDataParamType.Field));
    }


    /// <summary>
    /// Returns the parameters array formatted for multi-part/form data
    /// </summary>
    /// <returns></returns>
    public string GetPostData()
    {
        // Get boundary, default is --AaB03x
        string boundary = ConfigurationManager.AppSettings["ContentBoundary"].ToString();

        StringBuilder sb = new StringBuilder();
        foreach (PostDataParam p in m_Params)
        {
            sb.AppendLine(boundary);

            if (p.Type == PostDataParamType.File)
            {
                sb.AppendLine(string.Format("Content-Disposition: file; name=\"{0}\"; filename=\"{1}\"", p.Name, p.FileName));
                sb.AppendLine("Content-Type: text/plain");
                sb.AppendLine();
                sb.AppendLine(p.Value);                 
            }
            else
            {
                sb.AppendLine(string.Format("Content-Disposition: form-data; name=\"{0}\"", p.Name));
                sb.AppendLine();
                sb.AppendLine(p.Value);
            }
        }

        sb.AppendLine(boundary);

        return sb.ToString();           
    }
}

public enum PostDataParamType
{
    Field,
    File
}

public class PostDataParam
{


    public PostDataParam(string name, string value, PostDataParamType type)
    {
        Name = name;
        Value = value;
        Type = type;
    }

    public string Name;
    public string FileName;
    public string Value;
    public PostDataParamType Type;
}

Для отправки данных вам необходимо:

HttpWebRequest oRequest = null;
oRequest = (HttpWebRequest)HttpWebRequest.Create(oURL.URL);
oRequest.ContentType = "multipart/form-data";                       
oRequest.Method = "POST";
PostData pData = new PostData();

byte[] buffer = encoding.GetBytes(pData.GetPostData());

// Set content length of our data
oRequest.ContentLength = buffer.Length;

// Dump our buffered postdata to the stream, booyah
oStream = oRequest.GetRequestStream();
oStream.Write(buffer, 0, buffer.Length);
oStream.Close();

// get the response
oResponse = (HttpWebResponse)oRequest.GetResponse();

Надеюсь, что все ясно, я вырезал и вставил данные из нескольких источников, чтобы сделать их более аккуратными.

Я не думаю, что это работает, граница не определена в Content-Type, и нужно добавить слева «-» границы для каждого написанного и «-» справа от последнего один. Не спрашивайте меня, как появляется этот магический знак "-", они подразумеваются в официальной документации: w3.org/TR/html401/interact/forms.html#h-17.13.4.2 Если вам не нравятся магические требования, как мне, вам будет трудно заставить эту работу работать так, как я.

Daniel Lobo 31.03.2016 19:16

Этот код определенно работает для сценариев, в которых я его использовал. Однако я вижу, что, хотя я определяю полную границу для отправки в данных сообщения, я не указал добавление этого в общий заголовок содержимого в качестве ограничителя границы. Что касается магического числа, оно определяется клиентом, чтобы гарантировать, что оно уникально и нигде не будет отображаться внутри содержимого, подробнее см. Здесь: w3.org/Protocols/rfc1341/7_2_Multipart.html

dnolan 15.04.2016 12:04

Основываясь на примере dnolans, это та версия, с которой я действительно мог начать работать (были некоторые ошибки с границами, кодировка не была установлена) :-)

Чтобы отправить данные:

HttpWebRequest oRequest = null;
oRequest = (HttpWebRequest)HttpWebRequest.Create("http://you.url.here");
oRequest.ContentType = "multipart/form-data; boundary = " + PostData.boundary;
oRequest.Method = "POST";
PostData pData = new PostData();
Encoding encoding = Encoding.UTF8;
Stream oStream = null;

/* ... set the parameters, read files, etc. IE:
   pData.Params.Add(new PostDataParam("email", "[email protected]", PostDataParamType.Field));
   pData.Params.Add(new PostDataParam("fileupload", "filename.txt", "filecontents" PostDataParamType.File));
*/

byte[] buffer = encoding.GetBytes(pData.GetPostData());

oRequest.ContentLength = buffer.Length;

oStream = oRequest.GetRequestStream();
oStream.Write(buffer, 0, buffer.Length);
oStream.Close();

HttpWebResponse oResponse = (HttpWebResponse)oRequest.GetResponse();

Класс PostData должен выглядеть так:

public class PostData
{
    // Change this if you need to, not necessary
    public static string boundary = "AaB03x";

    private List<PostDataParam> m_Params;

    public List<PostDataParam> Params
    {
        get { return m_Params; }
        set { m_Params = value; }
    }

    public PostData()
    {
        m_Params = new List<PostDataParam>();
    }

    /// <summary>
    /// Returns the parameters array formatted for multi-part/form data
    /// </summary>
    /// <returns></returns>
    public string GetPostData()
    {
        StringBuilder sb = new StringBuilder();
        foreach (PostDataParam p in m_Params)
        {
            sb.AppendLine("--" + boundary);

            if (p.Type == PostDataParamType.File)
            {
                sb.AppendLine(string.Format("Content-Disposition: file; name=\"{0}\"; filename=\"{1}\"", p.Name, p.FileName));
                sb.AppendLine("Content-Type: application/octet-stream");
                sb.AppendLine();
                sb.AppendLine(p.Value);
            }
            else
            {
                sb.AppendLine(string.Format("Content-Disposition: form-data; name=\"{0}\"", p.Name));
                sb.AppendLine();
                sb.AppendLine(p.Value);
            }
        }

        sb.AppendLine("--" + boundary + "--");

        return sb.ToString();
    }
}

public enum PostDataParamType
{
    Field,
    File
}

public class PostDataParam
{
    public PostDataParam(string name, string value, PostDataParamType type)
    {
        Name = name;
        Value = value;
        Type = type;
    }

    public PostDataParam(string name, string filename, string value, PostDataParamType type)
    {
        Name = name;
        Value = value;
        FileName = filename;
        Type = type;
    }

    public string Name;
    public string FileName;
    public string Value;
    public PostDataParamType Type;
}

Мне нужно было смоделировать вход в браузер на веб-сайт, чтобы получить файл cookie для входа, а форма входа была multipart / form-data.

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

    public static class WebHelpers
    {
        /// <summary>
        /// Post the data as a multipart form
        /// </summary>
       public static HttpWebResponse MultipartFormDataPost(string postUrl, string userAgent, Dictionary<string, string> values)
       {
           string formDataBoundary = "---------------------------" + WebHelpers.RandomHexDigits(12);
           string contentType = "multipart/form-data; boundary = " + formDataBoundary;

           string formData = WebHelpers.MakeMultipartForm(values, formDataBoundary);
           return WebHelpers.PostForm(postUrl, userAgent, contentType, formData);
       }

        /// <summary>
        /// Post a form
        /// </summary>
        public static HttpWebResponse PostForm(string postUrl, string userAgent, string contentType, string formData)
        {
            HttpWebRequest request = WebRequest.Create(postUrl) as HttpWebRequest;

            if (request == null)
            {
                throw new NullReferenceException("request is not a http request");
            }

            // Add these, as we're doing a POST
            request.Method = "POST";
            request.ContentType = contentType;
            request.UserAgent = userAgent;
            request.CookieContainer = new CookieContainer();

            // We need to count how many bytes we're sending. 
            byte[] postBytes = Encoding.UTF8.GetBytes(formData);
            request.ContentLength = postBytes.Length;

            using (Stream requestStream = request.GetRequestStream())
            {
                // Push it out there
                requestStream.Write(postBytes, 0, postBytes.Length);
                requestStream.Close();
            }

            return request.GetResponse() as HttpWebResponse;
        }

        /// <summary>
        /// Generate random hex digits 
        /// </summary>
        public static string RandomHexDigits(int count)
        {
            Random random = new Random();
            StringBuilder result = new StringBuilder();
            for (int i = 0; i < count; i++)
            {
                int digit = random.Next(16);
                result.AppendFormat("{0:x}", digit);
            }

            return result.ToString();
        }

        /// <summary>
        /// Turn the key and value pairs into a multipart form
        /// </summary>
        private static string MakeMultipartForm(Dictionary<string, string> values, string boundary)
        {
            StringBuilder sb = new StringBuilder();

            foreach (var pair in values)
            {
                sb.AppendFormat("--{0}\r\nContent-Disposition: form-data; name=\"{1}\"\r\n\r\n{2}\r\n", boundary, pair.Key, pair.Value);
            }

            sb.AppendFormat("--{0}--\r\n", boundary);

            return sb.ToString();    
        }
    }
}

Он не обрабатывает данные файла, просто формирует, так как это все, что мне нужно. Я так звонил:

    try
    {
        using (HttpWebResponse response = WebHelpers.MultipartFormDataPost(postUrl, UserAgentString, this.loginForm)) 
        {
            if (response != null)
            {
                Cookie loginCookie = response.Cookies["logincookie"];
                .....

Всем спасибо за ответы! Недавно мне пришлось заставить это работать, и я активно использовал ваши предложения. Однако была пара хитрых частей, которые не работали должным образом, в основном связанные с фактическим включением файла (что было важной частью вопроса). Здесь уже есть много ответов, но я думаю, что это может быть кому-то полезно в будущем (я не смог найти много четких примеров этого в Интернете). Я написал сообщение в блоге, что объясняет это немного больше.

По сути, я сначала попытался передать данные файла в виде строки в кодировке UTF8, но у меня возникли проблемы с кодированием файлов (он работал нормально для простого текстового файла, но при загрузке документа Word, например, если я пытался сохранить файл, который был передан в опубликованную форму с помощью Request.Files [0] .SaveAs (), открытие файла в Word не работало должным образом. Я обнаружил, что если вы записываете данные файла напрямую с помощью Stream (а не StringBuilder ), все заработало, как и ожидалось.Кроме того, я внес несколько изменений, которые упростили понимание.

Между прочим, Многостраничные формы запроса комментариев и Рекомендация W3C для mulitpart / form-data - пара полезных ресурсов на случай, если кому-то понадобится ссылка на спецификацию.

Я изменил класс WebHelpers, чтобы он был немного меньше и имел более простые интерфейсы, теперь он называется FormUpload. Если вы передадите FormUpload.FileParameter, вы можете передать содержимое byte [] вместе с именем файла и типом контента, и если вы передадите строку, она будет рассматривать ее как стандартную комбинацию имя / значение.

Вот класс FormUpload:

// Implements multipart/form-data POST in C# http://www.ietf.org/rfc/rfc2388.txt
// http://www.briangrinstead.com/blog/multipart-form-post-in-c
public static class FormUpload
{
    private static readonly Encoding encoding = Encoding.UTF8;
    public static HttpWebResponse MultipartFormDataPost(string postUrl, string userAgent, Dictionary<string, object> postParameters)
    {
        string formDataBoundary = String.Format("----------{0:N}", Guid.NewGuid());
        string contentType = "multipart/form-data; boundary = " + formDataBoundary;

        byte[] formData = GetMultipartFormData(postParameters, formDataBoundary);

        return PostForm(postUrl, userAgent, contentType, formData);
    }
    private static HttpWebResponse PostForm(string postUrl, string userAgent, string contentType, byte[] formData)
    {
        HttpWebRequest request = WebRequest.Create(postUrl) as HttpWebRequest;

        if (request == null)
        {
            throw new NullReferenceException("request is not a http request");
        }

        // Set up the request properties.
        request.Method = "POST";
        request.ContentType = contentType;
        request.UserAgent = userAgent;
        request.CookieContainer = new CookieContainer();
        request.ContentLength = formData.Length;

        // You could add authentication here as well if needed:
        // request.PreAuthenticate = true;
        // request.AuthenticationLevel = System.Net.Security.AuthenticationLevel.MutualAuthRequested;
        // request.Headers.Add("Authorization", "Basic " + Convert.ToBase64String(System.Text.Encoding.Default.GetBytes("username" + ":" + "password")));

        // Send the form data to the request.
        using (Stream requestStream = request.GetRequestStream())
        {
            requestStream.Write(formData, 0, formData.Length);
            requestStream.Close();
        }

        return request.GetResponse() as HttpWebResponse;
    }

    private static byte[] GetMultipartFormData(Dictionary<string, object> postParameters, string boundary)
    {
        Stream formDataStream = new System.IO.MemoryStream();
        bool needsCLRF = false;

        foreach (var param in postParameters)
        {
            // Thanks to feedback from commenters, add a CRLF to allow multiple parameters to be added.
            // Skip it on the first parameter, add it to subsequent parameters.
            if (needsCLRF)
                formDataStream.Write(encoding.GetBytes("\r\n"), 0, encoding.GetByteCount("\r\n"));

            needsCLRF = true;

            if (param.Value is FileParameter)
            {
                FileParameter fileToUpload = (FileParameter)param.Value;

                // Add just the first part of this param, since we will write the file data directly to the Stream
                string header = string.Format("--{0}\r\nContent-Disposition: form-data; name=\"{1}\"; filename=\"{2}\";\r\nContent-Type: {3}\r\n\r\n",
                    boundary,
                    param.Key,
                    fileToUpload.FileName ?? param.Key,
                    fileToUpload.ContentType ?? "application/octet-stream");

                formDataStream.Write(encoding.GetBytes(header), 0, encoding.GetByteCount(header));

                // Write the file data directly to the Stream, rather than serializing it to a string.
                formDataStream.Write(fileToUpload.File, 0, fileToUpload.File.Length);
            }
            else
            {
                string postData = string.Format("--{0}\r\nContent-Disposition: form-data; name=\"{1}\"\r\n\r\n{2}",
                    boundary,
                    param.Key,
                    param.Value);
                formDataStream.Write(encoding.GetBytes(postData), 0, encoding.GetByteCount(postData));
            }
        }

        // Add the end of the request.  Start with a newline
        string footer = "\r\n--" + boundary + "--\r\n";
        formDataStream.Write(encoding.GetBytes(footer), 0, encoding.GetByteCount(footer));

        // Dump the Stream into a byte[]
        formDataStream.Position = 0;
        byte[] formData = new byte[formDataStream.Length];
        formDataStream.Read(formData, 0, formData.Length);
        formDataStream.Close();

        return formData;
    }

    public class FileParameter
    {
        public byte[] File { get; set; }
        public string FileName { get; set; }
        public string ContentType { get; set; }
        public FileParameter(byte[] file) : this(file, null) { }
        public FileParameter(byte[] file, string filename) : this(file, filename, null) { }
        public FileParameter(byte[] file, string filename, string contenttype)
        {
            File = file;
            FileName = filename;
            ContentType = contenttype;
        }
    }
}

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

// Read file data
FileStream fs = new FileStream("c:\\people.doc", FileMode.Open, FileAccess.Read);
byte[] data = new byte[fs.Length];
fs.Read(data, 0, data.Length);
fs.Close();

// Generate post objects
Dictionary<string, object> postParameters = new Dictionary<string, object>();
postParameters.Add("filename", "People.doc");
postParameters.Add("fileformat", "doc");
postParameters.Add("file", new FormUpload.FileParameter(data, "People.doc", "application/msword"));

// Create request and receive response
string postURL = "http://localhost";
string userAgent = "Someone";
HttpWebResponse webResponse = FormUpload.MultipartFormDataPost(postURL, userAgent, postParameters);

// Process response
StreamReader responseReader = new StreamReader(webResponse.GetResponseStream());
string fullResponse = responseReader.ReadToEnd();
webResponse.Close();
Response.Write(fullResponse);

Зрелищный. Я изменил, чтобы показать прогресс, поддержать запросы PUT и использовать свой собственный cookiecontainer, но это очень помогло мне на моем пути.

Jeff Hellman 05.08.2010 10:40

Отличная база. Я внес незначительные изменения, чтобы приспособиться к нашим потребностям, но определенно ОГРОМНО помог в многостраничной пограничной публикации. Значительно упростил мою работу. Спасибо

DRapp 23.04.2015 19:20

# Брайан Я точно следил за вашим кодом, но он не работает :(

Sunil Kumar S C 15.09.2015 08:41

так что файл читается в память перед отправкой, что если это большой файл

Benny 15.04.2019 08:39

Ниже приведен код, который я использую

    //This URL not exist, it's only an example.
    string url = "http://myBox.s3.amazonaws.com/";
    //Instantiate new CustomWebRequest class
    CustomWebRequest wr = new CustomWebRequest(url);
    //Set values for parameters
    wr.ParamsCollection.Add(new ParamsStruct("key", "${filename}"));
    wr.ParamsCollection.Add(new ParamsStruct("acl", "public-read"));
    wr.ParamsCollection.Add(new ParamsStruct("success_action_redirect", "http://www.yahoo.com"));
    wr.ParamsCollection.Add(new ParamsStruct("x-amz-meta-uuid", "14365123651274"));
    wr.ParamsCollection.Add(new ParamsStruct("x-amz-meta-tag", ""));
    wr.ParamsCollection.Add(new ParamsStruct("AWSAccessKeyId", "zzzz"));            
    wr.ParamsCollection.Add(new ParamsStruct("Policy", "adsfadsf"));
    wr.ParamsCollection.Add(new ParamsStruct("Signature", "hH6lK6cA = "));
    //For file type, send the inputstream of selected file
    StreamReader sr = new StreamReader(@"file.txt");
    wr.ParamsCollection.Add(new ParamsStruct("file", sr, ParamsStruct.ParamType.File, "file.txt"));

    wr.PostData();

по следующей ссылке я скачал тот же код http://www.codeproject.com/KB/cs/multipart_request_C_.aspx

Любая помощь

В той версии .NET, которую я использую, вам также необходимо сделать следующее:

System.Net.ServicePointManager.Expect100Continue = false;

Если вы этого не сделаете, класс HttpWebRequest автоматически добавит заголовок запроса Expect:100-continue, который все испортит.

Также я на собственном горьком опыте узнал, что у вас должно быть правильное количество тире. все, что вы говорите, это "граница" в заголовке Content-Type, должно предшествовать два дефиса

--THEBOUNDARY

и в конце

--THEBOUNDARY--

точно так же, как в примере кода. Если ваша граница состоит из множества дефисов, за которыми следует число, тогда эта ошибка не будет очевидна при просмотре http-запроса на прокси-сервере.

ЭТО ВАЖНО! Большое вам спасибо за упоминание об этом, я бы никогда не подумал об этом иначе, и это было единственное, что меня испортило!

ovinophile 11.03.2010 19:42

Небольшая оптимизация класса раньше. В этой версии файлы не загружаются в память полностью.

Совет по безопасности: проверка границы отсутствует, если файл содержит границу, произойдет сбой.

namespace WindowsFormsApplication1
{
    public static class FormUpload
    {
        private static string NewDataBoundary()
        {
            Random rnd = new Random();
            string formDataBoundary = "";
            while (formDataBoundary.Length < 15)
            {
                formDataBoundary = formDataBoundary + rnd.Next();
            }
            formDataBoundary = formDataBoundary.Substring(0, 15);
            formDataBoundary = "-----------------------------" + formDataBoundary;
            return formDataBoundary;
        }

        public static HttpWebResponse MultipartFormDataPost(string postUrl, IEnumerable<Cookie> cookies, Dictionary<string, string> postParameters)
        {
            string boundary = NewDataBoundary();

            HttpWebRequest request = (HttpWebRequest)WebRequest.Create(postUrl);

            // Set up the request properties
            request.Method = "POST";
            request.ContentType = "multipart/form-data; boundary = " + boundary;
            request.UserAgent = "PhasDocAgent 1.0";
            request.CookieContainer = new CookieContainer();

            foreach (var cookie in cookies)
            {
                request.CookieContainer.Add(cookie);
            }

            #region WRITING STREAM
            using (Stream formDataStream = request.GetRequestStream())
            {
                foreach (var param in postParameters)
                {
                    if (param.Value.StartsWith("file://"))
                    {
                        string filepath = param.Value.Substring(7);

                        // Add just the first part of this param, since we will write the file data directly to the Stream
                        string header = string.Format("--{0}\r\nContent-Disposition: form-data; name=\"{1}\"; filename=\"{2}\";\r\nContent-Type: {3}\r\n\r\n",
                            boundary,
                            param.Key,
                            Path.GetFileName(filepath) ?? param.Key,
                            MimeTypes.GetMime(filepath));

                        formDataStream.Write(Encoding.UTF8.GetBytes(header), 0, header.Length);

                        // Write the file data directly to the Stream, rather than serializing it to a string.

                        byte[] buffer = new byte[2048];

                        FileStream fs = new FileStream(filepath, FileMode.Open);

                        for (int i = 0; i < fs.Length; )
                        {
                            int k = fs.Read(buffer, 0, buffer.Length);
                            if (k > 0)
                            {
                                formDataStream.Write(buffer, 0, k);
                            }
                            i = i + k;
                        }
                        fs.Close();
                    }
                    else
                    {
                        string postData = string.Format("--{0}\r\nContent-Disposition: form-data; name=\"{1}\"\r\n\r\n{2}\r\n",
                            boundary,
                            param.Key,
                            param.Value);
                        formDataStream.Write(Encoding.UTF8.GetBytes(postData), 0, postData.Length);
                    }
                }
                // Add the end of the request
                byte[] footer = Encoding.UTF8.GetBytes("\r\n--" + boundary + "--\r\n");
                formDataStream.Write(footer, 0, footer.Length);
                request.ContentLength = formDataStream.Length;
                formDataStream.Close();
            }
            #endregion

            return request.GetResponse() as HttpWebResponse;
        }
    }
}

Спасибо за код, он сэкономил мне много времени (включая ошибку Except100!).

Во всяком случае, я обнаружил ошибку в коде, вот здесь:

formDataStream.Write(encoding.GetBytes(postData), 0, postData.Length);

Если ваши данные POST - это utf-16, postData.Length вернет количество символов, а не количество байтов. Это приведет к усечению публикуемых данных (например, если у вас есть 2 символа, которые закодированы как utf-16, они занимают 4 байта, но postData.Length скажет, что это занимает 2 байта, и вы потеряете 2 последних байта отправленного данные).

Решение - замените эту строку на:

byte[] aPostData=encoding.GetBytes(postData);
formDataStream.Write(aPostData, 0, aPostData.Length);

Используя это, длина рассчитывается по размеру байта [], а не по размеру строки.

encoding.GetByteCount (postData) может быть лучшим вариантом, если вам нужно только определить количество байтов

Marek 28.02.2011 19:17

Моя реализация

/// <summary>
/// Sending file via multipart\form-data
/// </summary>
/// <param name = "url">URL for send</param>
/// <param name = "file">Local file path</param>
/// <param name = "paramName">Request file param</param>
/// <param name = "contentType">Content-Type file headr</param>
/// <param name = "nvc">Additional post params</param>
private static string httpUploadFile(string url, string file, string paramName, string contentType, NameValueCollection nvc)
{
    //delimeter
    var boundary = "---------------------------" + DateTime.Now.Ticks.ToString("x");

    //creating request
    var wr = (HttpWebRequest)WebRequest.Create(url);
    wr.ContentType = "multipart/form-data; boundary = " + boundary;
    wr.Method = "POST";
    wr.KeepAlive = true;

    //sending request
    using(var requestStream = wr.GetRequestStream())
    {
        using (var requestWriter = new StreamWriter(requestStream, Encoding.UTF8))
        {
            //params
            const string formdataTemplate = "Content-Disposition: form-data; name=\"{0}\"\r\n\r\n{1}";
            foreach (string key in nvc.Keys)
            {
                requestWriter.Write(boundary);
                requestWriter.Write(String.Format(formdataTemplate, key, nvc[key]));
            }
            requestWriter.Write(boundary);

            //file header
            const string headerTemplate = "Content-Disposition: form-data; name=\"{0}\"; filename=\"{1}\"\r\nContent-Type: {2}\r\n\r\n";
            requestWriter.Write(String.Format(headerTemplate, paramName, file, contentType));

            //file content
            using (var fileStream = new FileStream(file, FileMode.Open, FileAccess.Read))
            {
                fileStream.CopyTo(requestStream);
            }

            requestWriter.Write("\r\n--" + boundary + "--\r\n");
        }
    }

    //reading response
    try
    {
        using (var wresp = (HttpWebResponse)wr.GetResponse())
        {
            if (wresp.StatusCode == HttpStatusCode.OK)
            {
                using (var responseStream = wresp.GetResponseStream())
                {
                    if (responseStream == null)
                        return null;
                    using (var responseReader = new StreamReader(responseStream))
                    {
                        return responseReader.ReadToEnd();
                    }
                }
            }

            throw new ApplicationException("Error while upload files. Server status code: " + wresp.StatusCode.ToString());
        }
    }
    catch (Exception ex)
    {
        throw new ApplicationException("Error while uploading file", ex);
    }
}

В настоящее время с .NET 4.5 вы можете использовать пространство имен System.Net.Http. Ниже приведен пример загрузки одного файла с использованием данных многостраничной формы.

using System;
using System.IO;
using System.Net.Http;

namespace HttpClientTest
{
    class Program
    {
        static void Main(string[] args)
        {
            var client = new HttpClient();
            var content = new MultipartFormDataContent();
            content.Add(new StreamContent(File.Open("../../Image1.png", FileMode.Open)), "Image", "Image.png");
            content.Add(new StringContent("Place string content here"), "Content-Id in the HTTP"); 
            var result = client.PostAsync("https://hostname/api/Account/UploadAvatar", content);
            Console.WriteLine(result.Result.ToString());
        }
    }
}

что, если я получаю подробную информацию о многостраничной форме в URL-адресе, здесь я спрашиваю о файлах, таких как pdf

Lutaaya Huzaifah Idris 04.05.2017 13:17

вы можете добавить больше содержимого данных формы (например, Id) с помощью content.Add (new StringContent ("1"), "MyId");

Daniël Tulp 02.10.2017 13:54

Другие вопросы по теме