C # HttpClient 4.5 Multipart- / Formulardaten-Upload

145

Weiß jemand, wie man das HttpClientin .Net 4.5 mit multipart/form-dataUpload benutzt?

Ich konnte im Internet keine Beispiele finden.

ident
quelle
1
Ich habe es versucht, aber ich habe keine Ahnung, wie ich es starten soll. Wo ich das byteArray zum Inhalt hinzufüge und so weiter. Ich brauche eine Art Starthilfe.
ident
Sie können diesen Beitrag Antwort suchen. (Mit Proxy-Einstellungen) stackoverflow.com/a/50462636/2123797
Ergin Çelik

Antworten:

156

Mein Ergebnis sieht so aus:

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
quelle
6
Wow, es ist so viel einfacher, dies zu tun, wenn große Dateien in die REST-API hochgeladen werden. Ich möchte nicht für Dank kommentieren, aber danke. Es ist tragbar für Windows Phone 8.
Léon Pelletier
1
Dieser Code ist für mich fehlgeschlagen, da die übergebene Grenzzeichenfolge new MultipartFormDataContent(...)ein ungültiges Grenzzeichen enthielt (möglicherweise das Trennzeichen "/"). Keine Fehler, nur keine Dateien auf dem Server - in meinem Fall Context.Request.Files.Count = 0 im API-Controller. Möglicherweise nur ein NancyProblem, aber ich schlage vor, stattdessen so etwas zu DateTime.Now.Ticks.ToString("x")verwenden.
Dunc
7
@MauricioAviles, dein Link ist kaputt. Ich fand dieses, das es schön erklärte: aspnetmonsters.com/2016/08/2016-08-27-httpclientwrong
Kevin Harker
1
Wenn Sie eine Fehlermeldung erhalten: „ Die hochgeladene Datei (en) nicht gefunden “ versuchen , die hinzuzufügen keyund fileNameParameter content( Bilddatei und upload.jpg in diesem Beispiel).
Jhhwilliams
1
@ KevinHarker, lies den zweiten Link noch einmal. Der Absatz, in dem davon gesprochen wurde, HttpClient nicht zu entsorgen, bezog sich auf das vorherige Design. Es ist leicht zu verwirren. Grundsätzlich macht die HttpClient Dispose mit der IHttpClientFactory nichts ( stackoverflow.com/a/54326424/476048 ) und die internen Handler werden von der HttpClientFactory verwaltet.
Berin Loritsch
83

Es funktioniert mehr oder weniger so (Beispiel mit einer Bild- / JPG-Datei):

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

(Sie können, requestContent.Add()was immer Sie wollen, einen Blick auf den Nachkommen von HttpContent werfen , um die verfügbaren Typen zum Übergeben zu sehen.)

Wenn Sie fertig sind, finden Sie den Antwortinhalt darin HttpResponseMessage.Content, den Sie verwenden können HttpContent.ReadAs*Async.

WDRust
quelle
2
Ahhh danke für die // here you can specify boundary if you need---^:)
sfarbota
1
warum funktioniert das nicht public async Task <string> SendImage (byte [] foto) {var requestContent = new MultipartFormDataContent (); var imageContent = neuer ByteArrayContent (Foto); imageContent.Headers.ContentType = MediaTypeHeaderValue.Parse ("image / jpeg"); requestContent.Add (imageContent, "foto", "foto.jpg"); string url = " myAddress / myWS / api / Home / SendImage? foto = "; warte auf _client.PostAsync (url, requestContent); return "ok"; }
Atapi19
1
asyncin der ersten Zeile und awaitin der Zeile vor der letzten sind nicht erforderlich.
1valdis
Fügen Sie bei großen Dateien der Anforderung einen Stream-Inhalt anstelle eines Byte-Arrays hinzu.
Elisabeth
1
@WDRust, mit einem Byte-Array laden Sie zuerst die gesamte Datei in den Speicher und senden sie dann. Bei einem Stream-Inhalt wird die Datei mit einem Puffer gelesen und gesendet, der speichereffizienter ist.
Josef Bláha
53

Dies ist ein Beispiel für das Posten von Zeichenfolgen und Dateistreams mit HTTPClient mithilfe von MultipartFormDataContent. Die Inhaltsdisposition und der Inhaltstyp müssen für jeden HTTP-Inhalt angegeben werden:

Hier ist mein Beispiel. Ich hoffe es hilft:

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
quelle
11
@Trout Du hast keine Ahnung, wie dein Code mich heute sooo glücklich gemacht hat! +1
Prise
6
Dies ist die vollständige Antwort.
VK
2
Ich weiß, wir sollten keinen Dankesbrief kommentieren. Aber das hier ist der beste Code, den ich je gesehen habe MultipartFormDataContent. Ein
großes Lob
Einverstanden. Dies ist die einzige Antwort, die JSON-Zeichenfolge und -Datei als Teil des Nutzdateninhalts enthält.
Frostshoxx
Ich teste auf meinem Computer (win7 sp1, IIS 7.5) ohne Content-Typeund Content-Dispositionist in Ordnung, aber auf Server 2008 R2 (IIS 7.5) kann ich keine Dateien finden, es ist seltsam. Also mache ich als Antwort.
Chengzi
18

Hier ist ein weiteres Beispiel HttpClientzum Hochladen von a multipart/form-data.

Es lädt eine Datei in eine REST-API hoch und enthält die Datei selbst (z. B. ein JPG) und zusätzliche API-Parameter. Die Datei wird direkt von der lokalen Festplatte über hochgeladenFileStream .

Sehen Sie hier für das gesamte Beispiel mit zusätzlichen API spezifische Logik.

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
quelle
12

Versuchen Sie das, es funktioniert für mich.

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
quelle
Makellos. Genau das, wonach ich in einem .NET Core- TestServer.CreatClient()Szenario eines Integrationstests für einen Daten- + Datei-Upload gesucht habe .
Vedran Mandić
Wenn die Methode HTTPGET ist, übergeben Sie den Formularinhalt
MBG
@MBG GET-Anforderungen haben normalerweise keinen Konventionstext, daher können Sie eine Datei nicht mit GET hochladen (oder nicht, es sei denn, der Server, an den Sie senden, ist sehr ungewöhnlich - die meisten Webserver würden dies nicht erwarten oder unterstützen). , weil es keinen Anforderungshauptteil gibt, in den entweder die Datei oder die zugehörigen Formulardaten aufgenommen werden können. Ich glaube , dass technisch es gibt nichts , was diese von getan in der Theorie verhindern würde, es ist nur , dass die Konvention über fast alle Implementierungen von HTTP , dass semantisch ist, Informationen in erster Linie für die Beschaffung ist (eher als das Senden) und so keinen Körper hat
ADyson
9

Hier ist ein vollständiges Beispiel, das für mich funktioniert hat. Der boundaryWert in der Anforderung wird automatisch von .NET hinzugefügt.

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
quelle
Wie können wir damit einen Token senden? Siehe dies bitte: stackoverflow.com/questions/48295877/…
@Softlion - Ich habe Probleme, es vor dem Senden NICHT in den Speicher zu laden. Wenn Sie einen besseren Weg kennen, posten Sie bitte hier: stackoverflow.com/questions/52446969/…
emery.noel
1

Beispiel mit Preloader 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
quelle
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.
quelle
Dieses Szenario wird für das Hochladen von Dateien auf eine API-Site mit Sicherheitszertifikat verwendet
Rajenthiran T
0

Ich füge ein Code-Snippet hinzu, das zeigt, wie eine Datei an eine API gesendet wird, die über das Verb DELETE http verfügbar gemacht wurde. Dies ist kein üblicher Fall, um eine Datei mit dem http-Verb DELETE hochzuladen, aber es ist zulässig. Ich habe die Windows NTLM-Authentifizierung für die Autorisierung des Anrufs angenommen.

Das Problem, mit dem man konfrontiert sein könnte, ist, dass alle Überladungen der HttpClient.DeleteAsyncMethode keine Parameter für HttpContentdie Art und Weise haben, wie wir sie in der PostAsyncMethode erhalten

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

Sie benötigen die folgenden Namespaces oben in Ihrer C # -Datei:

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

PS Tut mir leid, dass so viele Blöcke (IDisposable Pattern) in meinem Code verwendet werden. Leider unterstützt die Syntax der Verwendung des Konstrukts von C # nicht die Initialisierung mehrerer Variablen in einer einzelnen Anweisung.

RBT
quelle
-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;
}
Jack the Ripper
quelle
Sie können Ihre Antwort verbessern, indem Sie den Code kommentieren, den Sie geschrieben haben
msrd0
OK msrd! Entschuldigung für meinen Neuling. Ich versuche einen klaren Code wie "Erik Kalkoke" zu setzen, ich liebe es. Ich werde meinen Code wie das Empfangen eines Bildes von IFormFile auf Serverknoten 1 teilen und an Serverknoten 2 übergeben, indem ich etwas Text über die Klasse [MultipartFormDataContent] erhöhe. Oh! letzte Zeile wie diese. Ergebnis = warte auf res.Content.ReadAsStringAsync ();
Jack The Ripper