C # HttpClient 4.5 загрузка нескольких частей / данных формы

Кто-нибудь знает, как использовать HttpClient в .Net 4.5 с загрузкой multipart/form-data?

Я не нашел примеров в Интернете.


person ident    schedule 07.05.2013    source источник
comment
Я пробовал, но понятия не имею, как его запустить ... где я добавляю byteArray к содержимому и так далее. Мне нужна помощь для начала.   -  person ident    schedule 07.05.2013
comment
Вы можете посмотреть этот пост-ответ. (С настройками прокси) stackoverflow.com/a/50462636/2123797   -  person Ergin Çelik    schedule 22.05.2018


Ответы (10)


мой результат выглядит так:

public static async Task<string> Upload(byte[] image)
{
     using (var client = new HttpClient())
     {
         using (var content =
             new MultipartFormDataContent("Upload----" + DateTime.Now.ToString(CultureInfo.InvariantCulture)))
         {
             content.Add(new StreamContent(new MemoryStream(image)), "bilddatei", "upload.jpg");

              using (
                 var message =
                     await client.PostAsync("http://www.directupload.net/index.php?mode=upload", content))
              {
                  var input = await message.Content.ReadAsStringAsync();

                  return !string.IsNullOrWhiteSpace(input) ? Regex.Match(input, @"http://\w*\.directupload\.net/images/\d*/\w*\.[a-z]{3}").Value : null;
              }
          }
     }
}
person ident    schedule 16.05.2013
comment
Вау, это намного проще при загрузке больших файлов в REST API. Я не люблю комментировать "спасибо", но спасибо. Он переносится на Windows Phone 8. - person Léon Pelletier; 25.06.2013
comment
Этот код не удался для меня, поскольку граничная строка, переданная в new MultipartFormDataContent(...), содержала недопустимый граничный символ (возможно, разделитель /). Никаких ошибок, просто никаких файлов, отправленных на сервер - в моем случае Context.Request.Files.Count = 0 в контроллере API. Возможно, просто проблема Nancy, но я предлагаю вместо этого использовать что-то вроде DateTime.Now.Ticks.ToString("x"). - person Dunc; 04.09.2017
comment
@MauricioAviles, ваша ссылка не работает. Я нашел вот этот, который хорошо объяснил: https://aspnetmonsters.com/2016/08/2016-08-27-httpclientwrong/ - person Kevin Harker; 11.08.2018
comment
Если вы получаете сообщение об ошибке: Загруженные файлы не найдены, попробуйте добавить параметры key и fileName в content (bilddatei и upload.jpg в этом примере). - person jhhwilliams; 24.05.2019
comment
@KevinHarker, при наличии HttpClientFactory теперь безопасно работать с оператором using. Есть и другие ошибки, связанные с одним глобальным экземпляром HttpClient. - person Berin Loritsch; 11.07.2019
comment
@BerinLoritsch У вас есть статья об этом? Я все еще вижу ссылки, в которых говорится, что не следует избавляться от HttpClient. В частности, это не удаляет, но также не использует статические и Этот говорит не удалять - person Kevin Harker; 12.07.2019
comment
@KevinHarker, перечитай ту вторую ссылку. Абзац, в котором говорится об отказе от удаления HttpClient, относится к предыдущему дизайну. Это легко запутать. По сути, с IHttpClientFactory HttpClient Dispose на самом деле ничего не делает (stackoverflow.com/a/54326424/476048) а внутренние обработчики управляются HttpClientFactory. - person Berin Loritsch; 15.07.2019
comment
Я думаю, что суть этих статей в том, что на самом деле вы не должны заключать HttpClient в оператор using, подобный приведенному выше ответу. Об этом говорится в обеих моих недавних ссылках, и пример в статье, на которую вы ссылаетесь, сделал то же самое. Единственная разница заключается в том, что вы не должны хранить статический объект, а вместо этого должны позволить объекту располагаться так же, как и любой другой объект при использовании IHttpClientFactory. Спасибо @BerinLoritsch! Вероятно, нам следует правильно ввести новый ответ, используя IHttpClientFactory, поскольку это немного улучшает ситуацию. - person Kevin Harker; 15.07.2019

Это работает примерно так (пример с использованием файла image / jpg):

async public Task<HttpResponseMessage> UploadImage(string url, byte[] ImageData)
{
    var requestContent = new MultipartFormDataContent(); 
    //    here you can specify boundary if you need---^
    var imageContent = new ByteArrayContent(ImageData);
    imageContent.Headers.ContentType = 
        MediaTypeHeaderValue.Parse("image/jpeg");

    requestContent.Add(imageContent, "image", "image.jpg");

    return await client.PostAsync(url, requestContent);
}

(Вы можете requestContent.Add() что угодно, взгляните на потомок HttpContent чтобы увидеть доступные типы для передачи)

По завершении вы найдете содержимое ответа внутри HttpResponseMessage.Content, которое можно использовать с HttpContent.ReadAs*Async.

person WDRust    schedule 15.05.2013
comment
Ах, спасибо за // here you can specify boundary if you need---^ :) - person sfarbota; 10.09.2015
comment
почему это не работает? общедоступная асинхронная задача ‹string› SendImage (byte [] foto) {var requestContent = new MultipartFormDataContent (); var imageContent = новый ByteArrayContent (foto); imageContent.Headers.ContentType = MediaTypeHeaderValue.Parse (изображение / jpeg); requestContent.Add (imageContent, foto, foto.jpg); строка url = myAddress / myWS / api / Home / SendImage? foto =; ждать _client.PostAsync (url, requestContent); вернуть нормально; } - person atapi19; 29.01.2018
comment
async в первой строке и await в строке перед последней не нужны. - person 1valdis; 03.04.2018
comment
Для больших файлов добавьте к запросу содержимое потока, а не массив байтов. - person Elisabeth; 21.06.2018
comment
не могли бы вы рассказать, почему это может быть полезно @Elisabeth - person WDRust; 05.07.2018
comment
@WDRust, с байтовым массивом, вы сначала загружаете весь файл в память, а затем отправляете его. С содержимым потока файл считывается и отправляется с использованием буфера, который более эффективен с точки зрения памяти. - person Josef Bláha; 27.05.2019

Это пример того, как отправить строку и поток файла с HTTPClient с помощью MultipartFormDataContent. Content-Disposition и Content-Type необходимо указать для каждого HTTPContent:

Вот мой пример. Надеюсь, это поможет:

private static void Upload()
{
    using (var client = new HttpClient())
    {
        client.DefaultRequestHeaders.Add("User-Agent", "CBS Brightcove API Service");

        using (var content = new MultipartFormDataContent())
        {
            var path = @"C:\B2BAssetRoot\files\596086\596086.1.mp4";

            string assetName = Path.GetFileName(path);

            var request = new HTTPBrightCoveRequest()
                {
                    Method = "create_video",
                    Parameters = new Params()
                        {
                            CreateMultipleRenditions = "true",
                            EncodeTo = EncodeTo.Mp4.ToString().ToUpper(),
                            Token = "x8sLalfXacgn-4CzhTBm7uaCxVAPjvKqTf1oXpwLVYYoCkejZUsYtg..",
                            Video = new Video()
                                {
                                    Name = assetName,
                                    ReferenceId = Guid.NewGuid().ToString(),
                                    ShortDescription = assetName
                                }
                        }
                };

            //Content-Disposition: form-data; name="json"
            var stringContent = new StringContent(JsonConvert.SerializeObject(request));
            stringContent.Headers.Add("Content-Disposition", "form-data; name=\"json\"");
            content.Add(stringContent, "json");

            FileStream fs = File.OpenRead(path);

            var streamContent = new StreamContent(fs);
            streamContent.Headers.Add("Content-Type", "application/octet-stream");
            //Content-Disposition: form-data; name="file"; filename="C:\B2BAssetRoot\files\596090\596090.1.mp4";
            streamContent.Headers.Add("Content-Disposition", "form-data; name=\"file\"; filename=\"" + Path.GetFileName(path) + "\"");
            content.Add(streamContent, "file", Path.GetFileName(path));

            //content.Headers.ContentDisposition = new ContentDispositionHeaderValue("attachment");

            Task<HttpResponseMessage> message = client.PostAsync("http://api.brightcove.com/services/post", content);

            var input = message.Result.Content.ReadAsStringAsync();
            Console.WriteLine(input.Result);
            Console.Read();
        }
    }
}
person Johnny Chu    schedule 30.01.2015
comment
@Trout Ты не представляешь, как твой код сделал меня сегодня ооочень счастливым! +1 - person Pinch; 01.06.2015
comment
Это полный ответ. - person V K; 13.09.2016
comment
Я знаю, что мы не должны комментировать благодарственную записку. Но вот лучший код, который я видел, о том, как использовать MultipartFormDataContent. Престижность вам, сэр - person sebagomez; 04.10.2016
comment
Согласовано. Это единственный ответ, который включает строку и файл json как часть содержимого полезной нагрузки. - person frostshoxx; 28.02.2018
comment
Я тестирую на своем компьютере (win7 sp1, IIS 7.5) без Content-Type и Content-Disposition в порядке, но на Server 2008 R2 (IIS 7.5) не могу найти файлы, это странно. Так что я делаю ответ. - person chengzi; 28.03.2018
comment
Это спасло мне день .. именно то, что мне было нужно! Престижность !!!! Спасибо за ваши усилия @Johnny Chu - person Afzal Ali; 02.09.2019

Вот еще один пример того, как использовать HttpClient для загрузки multipart/form-data.

Он загружает файл в REST API и включает сам файл (например, JPG) и дополнительные параметры API. Файл загружается напрямую с локального диска через FileStream.

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

public static async Task UploadFileAsync(string token, string path, string channels)
{
    // we need to send a request with multipart/form-data
    var multiForm = new MultipartFormDataContent();

    // add API method parameters
    multiForm.Add(new StringContent(token), "token");
    multiForm.Add(new StringContent(channels), "channels");

    // add file and directly upload it
    FileStream fs = File.OpenRead(path);
    multiForm.Add(new StreamContent(fs), "file", Path.GetFileName(path));

    // send request to API
    var url = "https://slack.com/api/files.upload";
    var response = await client.PostAsync(url, multiForm);
}
person Erik Kalkoken    schedule 13.11.2018

Попробуй, это сработает для меня.

private static async Task<object> Upload(string actionUrl)
{
    Image newImage = Image.FromFile(@"Absolute Path of image");
    ImageConverter _imageConverter = new ImageConverter();
    byte[] paramFileStream= (byte[])_imageConverter.ConvertTo(newImage, typeof(byte[]));

    var formContent = new MultipartFormDataContent
    {
        // Send form text values here
        {new StringContent("value1"),"key1"},
        {new StringContent("value2"),"key2" },
        // Send Image Here
        {new StreamContent(new MemoryStream(paramFileStream)),"imagekey","filename.jpg"}
    };

    var myHttpClient = new HttpClient();
    var response = await myHttpClient.PostAsync(actionUrl.ToString(), formContent);
    string stringContent = await response.Content.ReadAsStringAsync();

    return response;
}
person Vishnu Kumar    schedule 07.11.2018
comment
Безупречный. Именно то, что я искал в сценарии .NET Core TestServer.CreatClient() интеграционного теста для загрузки данных + файла. - person Vedran Mandić; 20.11.2019
comment
если метод HTTPGET, как передать formcontent - person dev; 09.07.2020
comment
Запросы @MBG GET обычно не имеют тела запроса по соглашению, поэтому вы не можете загрузить файл с помощью GET (или нет, если сервер, на который вы отправляете, не очень необычный - большинство веб-серверов не ожидают этого или не поддерживают его) , потому что нет тела запроса, в которое можно было бы включить файл или сопутствующие данные формы. Я считаю, что технически нет ничего, что могло бы помешать это сделать теоретически, просто соглашение почти во всех реализациях HTTP состоит в том, что семантически GET в первую очередь предназначен для получения информации (а не для отправки) и поэтому не имеет тела - person ADyson; 17.07.2020

Вот полный образец, который у меня сработал. Значение boundary в запросе автоматически добавляется .NET.

var url = "http://localhost/api/v1/yourendpointhere";
var filePath = @"C:\path\to\image.jpg";

HttpClient httpClient = new HttpClient();
MultipartFormDataContent form = new MultipartFormDataContent();

FileStream fs = File.OpenRead(filePath);
var streamContent = new StreamContent(fs);

var imageContent = new ByteArrayContent(streamContent.ReadAsByteArrayAsync().Result);
imageContent.Headers.ContentType = MediaTypeHeaderValue.Parse("multipart/form-data");

form.Add(imageContent, "image", Path.GetFileName(filePath));
var response = httpClient.PostAsync(url, form).Result;
person nthpixel    schedule 08.05.2017
comment
Как мы можем отправить токен с этим? См. Это, пожалуйста: stackoverflow.com / questions / 48295877 / - person ; 17.01.2018
comment
@Softlion - У меня проблема НЕ загружается в память перед отправкой. Если вы знаете способ получше, напишите здесь: stackoverflow.com/questions/52446969/ - person emery.noel; 21.09.2018

Пример с прелоадером Dotnet 3.0 Core

ProgressMessageHandler processMessageHander = new ProgressMessageHandler();

processMessageHander.HttpSendProgress += (s, e) =>
{
    if (e.ProgressPercentage > 0)
    {
        ProgressPercentage = e.ProgressPercentage;
        TotalBytes = e.TotalBytes;
        progressAction?.Invoke(progressFile);
    }
};

using (var client = HttpClientFactory.Create(processMessageHander))
{
    var uri = new Uri(transfer.BackEndUrl);
    client.DefaultRequestHeaders.Authorization =
    new AuthenticationHeaderValue("Bearer", AccessToken);

    using (MultipartFormDataContent multiForm = new MultipartFormDataContent())
    {
        multiForm.Add(new StringContent(FileId), "FileId");
        multiForm.Add(new StringContent(FileName), "FileName");
        string hash = "";

        using (MD5 md5Hash = MD5.Create())
        {
            var sb = new StringBuilder();
            foreach (var data in md5Hash.ComputeHash(File.ReadAllBytes(FullName)))
            {
                sb.Append(data.ToString("x2"));
            }
            hash = result.ToString();
        }
        multiForm.Add(new StringContent(hash), "Hash");

        using (FileStream fs = File.OpenRead(FullName))
        {
            multiForm.Add(new StreamContent(fs), "file", Path.GetFileName(FullName));
            var response = await client.PostAsync(uri, multiForm);
            progressFile.Message = response.ToString();

            if (response.IsSuccessStatusCode) {
                progressAction?.Invoke(progressFile);
            } else {
                progressErrorAction?.Invoke(progressFile);
            }
            response.EnsureSuccessStatusCode();
        }
    }
}
person D.Oleg    schedule 12.12.2019

Я добавляю фрагмент кода, который показывает, как отправить файл в API, который был открыт через HTTP-команду DELETE. Это не обычный случай загрузки файла с помощью HTTP-команды DELETE, но это разрешено. Я предположил, что для авторизации звонка используется аутентификация Windows NTLM.

Проблема, с которой можно столкнуться, заключается в том, что все перегрузки метода HttpClient.DeleteAsync не имеют параметров для HttpContent, как мы его получаем в методе PostAsync.

var requestUri = new Uri("http://UrlOfTheApi");
using (var streamToPost = new MemoryStream("C:\temp.txt"))
using (var fileStreamContent = new StreamContent(streamToPost))
using (var httpClientHandler = new HttpClientHandler() { UseDefaultCredentials = true })
using (var httpClient = new HttpClient(httpClientHandler, true))
using (var requestMessage = new HttpRequestMessage(HttpMethod.Delete, requestUri))
using (var formDataContent = new MultipartFormDataContent())
{
    formDataContent.Add(fileStreamContent, "myFile", "temp.txt");
    requestMessage.Content = formDataContent;
    var response = httpClient.SendAsync(requestMessage).GetAwaiter().GetResult();

    if (response.IsSuccessStatusCode)
    {
        // File upload was successfull
    }
    else
    {
        var erroResult = response.Content.ReadAsStringAsync().GetAwaiter().GetResult();
        throw new Exception("Error on the server : " + erroResult);
    }
}

Вам нужны следующие пространства имен в верхней части файла C #:

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

P.S. Извините за то, что в моем коде так много блоков (шаблон IDisposable). К сожалению, синтаксис использования конструкции C # не поддерживает инициализацию нескольких переменных в одном операторе.

person RBT    schedule 28.09.2019

person    schedule
comment
Этот сценарий используется для загрузки файла на сайт API с сертификатом безопасности. - person Rajenthiran T; 20.09.2019

person    schedule
comment
Вы можете улучшить свой ответ, прокомментировав написанный вами код. - person msrd0; 11.08.2019
comment
ОК, msrd! Сожалею о моем послушнике. Я стараюсь писать четкий код, как Эрик Калкок, мне это нравится. Я поделюсь своим кодом, например, получу изображение от IFormFile на серверном узле 1 и передам на серверный узел 2, увеличив текст через класс [MultipartFormDataContent] О! последняя строка вроде этой. результат = ждать res.Content.ReadAsStringAsync (); - person Jack The Ripper; 12.08.2019