C # HttpClient 4.5 caricamento multipart / form-data


145

Qualcuno sa come utilizzare HttpClientin .Net 4.5 con multipart/form-dataupload?

Non sono riuscito a trovare esempi su Internet.


1
Ho provato ma non ho idea di come avviarlo .. dove aggiungo il byteArray al contenuto e così via. ho bisogno di un aiuto iniziale.
ident

Puoi vedere questa risposta al post. (Con impostazioni proxy) stackoverflow.com/a/50462636/2123797
Ergin Çelik

Risposte:


156

il mio risultato è simile al seguente:

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

6
Wow, è molto più semplice farlo quando si caricano file di grandi dimensioni nell'API REST. Non mi piace commentare per grazie, ma grazie. È portatile per Windows Phone 8.
Léon Pelletier il

1
Questo codice non è riuscito per me poiché la stringa di limite passava a new MultipartFormDataContent(...)contenere un carattere di limite non valido (forse il separatore "/"). Nessun errore, solo nessun file pubblicato sul server - nel mio caso, Context.Request.Files.Count = 0 nel controller API. Forse solo un Nancyproblema, ma suggerisco di usare qualcosa di simile DateTime.Now.Ticks.ToString("x")invece.
Dunc,

7
@MauricioAviles, il tuo link è interrotto. Ho trovato questo che lo ha spiegato bene: aspnetmonsters.com/2016/08/2016 08-08-27
Kevin Harker,

1
Se ricevi un errore: " File caricati non trovati " prova ad aggiungere i parametri keye fileNamea content( bilddatei e upload.jpg in questo esempio).
jhhwilliams

1
@KevinHarker, rileggi quel secondo link. Il paragrafo che parla di non eliminare HttpClient si riferiva al design precedente. È facile da confondere. Fondamentalmente, con IHttpClientFactory, HttpClient Dispose non fa davvero nulla ( stackoverflow.com/a/54326424/476048 ) e i gestori interni sono gestiti da HttpClientFactory.
Berin Loritsch,

83

Funziona più o meno così (esempio usando un file immagine / 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);
}

(Puoi fare requestContent.Add()quello che vuoi, dai un'occhiata al discendente di HttpContent per vedere i tipi disponibili da trasmettere)

Al termine, troverai il contenuto della risposta all'interno del HttpResponseMessage.Contentquale puoi consumare HttpContent.ReadAs*Async.


2
Ahhh grazie per il // here you can specify boundary if you need---^:)
sfarbota,

1
perché questo non funziona? 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); ritorna "ok"; }
atapi19

1
asyncsulla prima riga e awaitsulla riga prima dell'ultima non sono necessarie.
1valdis,

Per file di grandi dimensioni, aggiungere un contenuto di flusso alla richiesta anziché un array di byte.
Elisabeth,

1
@WDRust, con un array di byte, prima carichi l'intero file in memoria e poi lo invii. Con un contenuto di flusso, il file viene letto e inviato utilizzando un buffer, che è più efficiente in termini di memoria.
Josef Bláha,

53

Questo è un esempio di come inviare un flusso di stringhe e file con HTTPClient usando MultipartFormDataContent. La disposizione del contenuto e il tipo di contenuto devono essere specificati per ciascun contenuto HTTP:

Ecco il mio esempio Spero che sia d'aiuto:

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

11
@Trout Non hai idea di come il tuo codice mi abbia reso così felice oggi! +1
Pizzica il

6
Questa è la risposta completa.
VK,

2
So che non dovremmo commentare una nota di ringraziamento. Ma questo qui è il miglior codice che ho visto su come usare MultipartFormDataContent. Complimenti, signore
sebagomez,

Concordato. Questa è l'unica risposta che include stringa e file json come parte del contenuto del payload.
frostshoxx,

Provo sul mio computer (win7 sp1, IIS 7.5) senza Content-Typee Content-Dispositionva bene, ma su Server 2008 R2 (IIS 7.5) non riesco a trovare i file, è strano. Quindi faccio come risposta.
chengzi,

18

Ecco un altro esempio su come utilizzare HttpClientper caricare un multipart/form-data.

Carica un file su un'API REST e include il file stesso (ad esempio un JPG) e parametri API aggiuntivi. Il file viene caricato direttamente dal disco locale tramite FileStream.

Vedi qui per l'esempio completo che include la logica aggiuntiva API specifica.

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

12

Prova questo funziona per me.

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

Flawless. Esattamente quello che stavo cercando in uno TestServer.CreatClient()scenario .NET Core di un test di integrazione per un caricamento di dati + file.
Vedran Mandić,

se il metodo è HTTPGET come passare il contenuto
MBG

Le richieste GET di @MBG normalmente non hanno un corpo di richiesta per convenzione, quindi non puoi caricare un file usando GET (o meno a meno che il server a cui stai inviando sia molto insolito - la maggior parte dei server web non se lo aspettano o lo supportano) , poiché non esiste un corpo della richiesta in cui includere i dati del file o del modulo di accompagnamento. Credo che tecnicamente non ci sia nulla che possa impedire che ciò avvenga in teoria, è solo che la convenzione su quasi tutte le implementazioni di HTTP è che semanticamente, GET è principalmente per il recupero di informazioni (piuttosto che per l'invio) e quindi non ha un corpo
ADyson,

9

Ecco un esempio completo che ha funzionato per me. Il boundaryvalore nella richiesta viene aggiunto automaticamente da .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;

Come possiamo inviare un token con questo? Vedere questo per favore: stackoverflow.com/questions/48295877/...

@Softlion - Non riesco a caricarlo nella memoria prima di inviarlo. Se conoscete un modo migliore, si prega di postare qui: stackoverflow.com/questions/52446969/...
emery.noel

1

Esempio con 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();
        }
    }
}

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

Questo scenario viene utilizzato per il caricamento di file nel sito API con certificato di sicurezza
Rajenthiran T

0

Sto aggiungendo uno snippet di codice che mostra come inviare un file a un'API che è stata esposta tramite il comando DELETE http verbo. Questo non è un caso comune per caricare un file con il comando DELETE http verbo ma è permesso. Ho assunto l'autenticazione di Windows NTLM per autorizzare la chiamata.

Il problema che si potrebbe incontrare è che tutti i sovraccarichi del HttpClient.DeleteAsyncmetodo non hanno parametri per HttpContentil modo in cui lo otteniamo nel PostAsyncmetodo

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

È necessario sotto gli spazi dei nomi nella parte superiore del file C #:

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

PS Mi dispiace per così tanti blocchi usando (modello IDisposable) nel mio codice. Sfortunatamente, la sintassi dell'uso del costrutto di C # non supporta l'inizializzazione di più variabili in una singola istruzione.


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

Puoi migliorare la tua risposta commentando il codice che hai scritto
msrd0

OK msrd! Mi dispiace per il mio principiante. Provo a mettere un codice chiaro come "Erik Kalkoke", lo adoro. condividerò il mio codice come ricevere l'immagine da IFormFile sul nodo server 1 e passerò al nodo server 2 aumentando del testo tramite la classe [MultipartFormDataContent] Oh! ultima riga come questa. risultato = await res.Content.ReadAsStringAsync ();
Jack The Ripper,
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.