C # HttpClient 4,5 przesyłanie danych wieloczęściowych / formularzy

145

Czy ktoś wie, jak korzystać z HttpClient.Net 4.5 z multipart/form-datauploadem?

Nie mogłem znaleźć żadnych przykładów w internecie.

ident
źródło
1
Próbowałem, ale nie mam pojęcia, jak to rozpocząć .. gdzie dodam byteArray do zawartości i tak dalej. Potrzebuję pomocy na rozpoczęcie.
ident
Możesz spojrzeć na tę odpowiedź na post. (Z ustawieniami proxy) stackoverflow.com/a/50462636/2123797
Ergin Çelik

Odpowiedzi:

156

mój wynik wygląda następująco:

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;
              }
          }
     }
}
ident
źródło
6
Wow, jest to o wiele prostsze podczas przesyłania dużych plików do REST API. Nie lubię komentować podziękowań, ale dzięki. Jest przenośny dla Windows Phone 8.
Léon Pelletier,
1
Ten kod nie powiódł się, ponieważ przekazany ciąg graniczny new MultipartFormDataContent(...)zawierał nieprawidłowy znak granicy (może to być separator „/”). Żadnych błędów, po prostu żadnych plików wysłanych na serwer - w moim przypadku Context.Request.Files.Count = 0 w kontrolerze API. Prawdopodobnie tylko Nancyproblem, ale sugeruję użycie czegoś takiegoDateTime.Now.Ticks.ToString("x") .
Dunc
7
@MauricioAviles, Twój link jest uszkodzony. Znalazłem ten, który ładnie to wyjaśnił: aspnetmonsters.com/2016/08/2016-08-27-httpclientwrong
Kevin Harker
1
Jeśli pojawi się błąd: „ Nie znaleziono przesłanych plików ”, spróbuj dodać parametry keyi fileNamedo content( w tym przykładzie bilddatei i upload.jpg ).
jhhwilliams
1
@KevinHarker, Przeczytaj ponownie ten drugi link. Akapit dotyczący niewyrzucania HttpClient odnosił się do poprzedniego projektu. Łatwo to pomylić. Zasadniczo w przypadku IHttpClientFactory, HttpClient Dispose tak naprawdę nic nie robi ( stackoverflow.com/a/54326424/476048 ), a wewnętrzne programy obsługi są zarządzane przez HttpClientFactory.
Berin Loritsch
83

Działa mniej więcej tak (przykład z użyciem pliku 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);
}

(Możesz requestContent.Add()cokolwiek chcesz, spójrz na element podrzędny HttpContent aby zobaczyć dostępne typy do przekazania)

Po zakończeniu znajdziesz treść odpowiedzi w środku HttpResponseMessage.Content , z którą możesz skonsumować HttpContent.ReadAs*Async.

WDRust
źródło
2
Ahhh dzięki za // here you can specify boundary if you need---^:)
sfarbota
1
dlaczego to nie działa? public async Task <string> SendImage (byte [] foto) {var requestContent = new MultipartFormDataContent (); var imageContent = new ByteArrayContent (foto); imageContent.Headers.ContentType = MediaTypeHeaderValue.Parse ("image / jpeg"); requestContent.Add (imageContent, "foto", "foto.jpg"); string url = " myAddress / myWS / api / Home / SendImage? foto = "; await _client.PostAsync (url, requestContent); return "ok"; }
atapi19
1
asyncna pierwszej linii i awaitna linii przed ostatnią są zbędne.
1valdis
W przypadku dużych plików dodaj zawartość strumienia do żądania, a nie tablicę bajtów.
Elisabeth
1
@WDRust, z tablicą bajtów, najpierw ładujesz cały plik do pamięci, a następnie wysyłasz. W przypadku zawartości strumienia plik jest odczytywany i wysyłany przy użyciu bufora, który jest bardziej wydajny pod względem pamięci.
Josef Bláha
53

To jest przykład publikowania strumienia ciągu i pliku za pomocą HTTPClient przy użyciu MultipartFormDataContent. Dyspozycja zawartości i typ zawartości muszą być określone dla każdego elementu HTTPContent:

Oto mój przykład. Mam nadzieję, że to pomoże:

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();
        }
    }
}
Johnny Chu
źródło
11
@Trout Nie masz pojęcia, jak Twój kod sprawił, że jestem dziś taki szczęśliwy! +1
Pinch
6
To jest pełna odpowiedź.
VK,
2
Wiem, że nie powinniśmy komentować listu z podziękowaniami. Ale tutaj jest najlepszy kod, jaki widziałem, jak używać MultipartFormDataContent. Uznanie dla pana
sebagomez
Zgoda. To jedyna odpowiedź, która zawiera ciąg i plik json jako część zawartości ładunku.
frostshoxx
Testuję na moim komputerze (win7 sp1, IIS 7.5) bez Content-Typei Content-Dispositionjest OK, ale na serwerze 2008 R2 (IIS 7.5) nie mogę znaleźć plików, to dziwne. Więc robię jako odpowiedź.
Chengzi
18

Oto kolejny przykład użycia HttpClientdo przesłania plikumultipart/form-data .

Przesyła plik do REST API i zawiera sam plik (np. JPG) oraz dodatkowe parametry API. Plik jest przesyłany bezpośrednio z dysku lokalnego za pośrednictwemFileStream .

Zobacz tutaj pełny przykład, w tym dodatkową logikę specyficzną dla interfejsu 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);
}
Erik Kalkoken
źródło
12

Spróbuj, to działa dla mnie.

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;
}
Vishnu Kumar
źródło
Bez skazy. Dokładnie to, czego szukałem w TestServer.CreatClient()scenariuszu .NET Core testu integracji dla przesyłania danych + pliku.
Vedran Mandić
jeśli metodą jest HTTPGET, jak przekazać zawartość formularza
MBG
Żądania @MBG GET zwykle nie mają treści żądania zgodnie z konwencją, więc nie możesz przesłać pliku za pomocą GET (lub nie, chyba że serwer, do którego wysyłasz, jest bardzo nietypowy - większość serwerów internetowych nie spodziewa się tego ani nie obsługuje) , ponieważ nie ma treści żądania, w której można by uwzględnić plik lub towarzyszące mu dane formularza. Uważam, że technicznie nie ma nic, co mogłoby przeszkodzić temu w teorii, po prostu konwencja w prawie wszystkich implementacjach HTTP jest taka, że ​​semantycznie GET służy głównie do pobierania informacji (zamiast wysyłania), a więc nie ma treści
ADyson
9

Oto pełna próbka, która zadziałała dla mnie. boundaryWartość w żądaniu jest dodawany automatycznie przez .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;
nthpixel
źródło
Jak możemy wysłać token z tym? Zobacz to proszę: stackoverflow.com/questions/48295877/…
@Softlion - Mam problem z NIE wczytaniem go do pamięci przed wysłaniem. Jeśli znasz lepszy sposób, napisz tutaj: stackoverflow.com/questions/52446969/ ...
emery.noel
1

Przykład z preloaderem 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();
        }
    }
}
D.Oleg
źródło
1
X509Certificate clientKey1 = null;
clientKey1 = new X509Certificate(AppSetting["certificatePath"],
AppSetting["pswd"]);
string url = "https://EndPointAddress";
FileStream fs = File.OpenRead(FilePath);
var streamContent = new StreamContent(fs);

var FileContent = new ByteArrayContent(streamContent.ReadAsByteArrayAsync().Result);
FileContent.Headers.ContentType = MediaTypeHeaderValue.Parse("ContentType");
var handler = new WebRequestHandler();


handler.ClientCertificateOptions = ClientCertificateOption.Manual;
handler.ClientCertificates.Add(clientKey1);
handler.ServerCertificateValidationCallback = (httpRequestMessage, cert, cetChain, policyErrors) =>
{
    return true;
};


using (var client = new HttpClient(handler))
{
    // Post it
    HttpResponseMessage httpResponseMessage = client.PostAsync(url, FileContent).Result;

    if (!httpResponseMessage.IsSuccessStatusCode)
    {
        string ss = httpResponseMessage.StatusCode.ToString();
    }
}
Rajenthiran T
źródło
Ten scenariusz służy do przesyłania plików do witryny API z certyfikatem bezpieczeństwa
Rajenthiran T
0

Dodaję fragment kodu, który pokazuje, jak wysłać plik do interfejsu API, który został ujawniony przez czasownik USUŃ http. Nie jest to typowy przypadek przesyłania pliku z czasownikiem DELETE http, ale jest to dozwolone. Założyłem uwierzytelnianie Windows NTLM do autoryzacji połączenia.

Problem, z którym można się spotkać, polega na tym, że wszystkie przeciążenia HttpClient.DeleteAsyncmetody nie mają parametrów dotyczących HttpContentsposobu, w jaki otrzymujemy ją w PostAsyncmetodzie

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

Potrzebujesz poniższych przestrzeni nazw w górnej części pliku C #:

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

PS Przepraszam, że tak wielu używa bloków (wzorzec IDisposable) w moim kodzie. Niestety, składnia using konstruktu języka C # nie obsługuje inicjowania wielu zmiennych w jednej instrukcji.

RBT
źródło
-3
public async Task<object> PassImageWithText(IFormFile files)
{
    byte[] data;
    string result = "";
    ByteArrayContent bytes;

    MultipartFormDataContent multiForm = new MultipartFormDataContent();

    try
    {
        using (var client = new HttpClient())
        {
            using (var br = new BinaryReader(files.OpenReadStream()))
            {
                data = br.ReadBytes((int)files.OpenReadStream().Length);
            }

            bytes = new ByteArrayContent(data);
            multiForm.Add(bytes, "files", files.FileName);
            multiForm.Add(new StringContent("value1"), "key1");
            multiForm.Add(new StringContent("value2"), "key2");

            var res = await client.PostAsync(_MEDIA_ADD_IMG_URL, multiForm);
        }
    }
    catch (Exception e)
    {
        throw new Exception(e.ToString());
    }

    return result;
}
Kuba Rozpruwacz
źródło
Możesz poprawić swoją odpowiedź, komentując napisany przez siebie kod
msrd0
OK msrd! Przepraszam za mojego nowicjusza. Próbuję napisać jasny kod, jak „Erik Kalkoke”, uwielbiam to. udostępnię swój kod, na przykład otrzymam obraz przez IFormFile w węźle serwera 1 i przekażę do węzła serwera 2, zwiększając część tekstu za pomocą klasy [MultipartFormDataContent] Och! ostatnia linijka w ten sposób. wynik = await res.Content.ReadAsStringAsync ();
Jack The Ripper