C # HttpClient 4.5 téléchargement multipart / form-data

145

Quelqu'un sait-il comment utiliser le HttpClient.Net 4.5 avec le multipart/form-datatéléchargement?

Je n'ai trouvé aucun exemple sur Internet.

ident
la source
1
J'ai essayé mais je n'ai aucune idée de comment le démarrer .. où j'ajoute le byteArray au contenu et ainsi de suite. j'ai besoin d'une sorte d'aide au démarrage.
ident
Vous pouvez regarder cette réponse post. (Avec les paramètres de proxy) stackoverflow.com/a/50462636/2123797
Ergin Çelik

Réponses:

156

mon résultat ressemble à ceci:

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
la source
6
Wow, c'est tellement plus simple de le faire lors du téléchargement de gros fichiers vers l'API REST. Je n'aime pas commenter pour merci, mais merci. Il est portable pour Windows Phone 8.
Léon Pelletier
1
Ce code a échoué pour moi car la chaîne de limite passée à new MultipartFormDataContent(...)contenait un caractère de limite non valide (peut-être le séparateur "/"). Aucune erreur, juste aucun fichier publié sur le serveur - dans mon cas, Context.Request.Files.Count = 0 dans le contrôleur d'API. Peut-être juste un Nancyproblème, mais je suggère d'utiliser quelque chose comme à la DateTime.Now.Ticks.ToString("x")place.
Dunc
7
@MauricioAviles, votre lien est rompu. J'ai trouvé celui-ci qui l'a bien expliqué: aspnetmonsters.com/2016/08/2016-08-27-httpclientwrong
Kevin Harker
1
Si vous obtenez une erreur: " Fichier (s) téléchargé (s) introuvable (s) ", essayez d'ajouter les paramètres keyet fileNameà content( bilddatei et upload.jpg dans cet exemple).
jhhwilliams
1
@KevinHarker, relisez ce deuxième lien. Le paragraphe parlant de ne pas supprimer HttpClient faisait référence à la conception précédente. Il est facile de confondre. Fondamentalement, avec IHttpClientFactory, HttpClient Dispose ne fait vraiment rien ( stackoverflow.com/a/54326424/476048 ) et les gestionnaires internes sont gérés par HttpClientFactory.
Berin Loritsch
83

Cela fonctionne plus ou moins comme ceci (exemple en utilisant un fichier 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);
}

(Vous pouvez requestContent.Add()tout ce que vous voulez, jetez un œil au descendant de HttpContent pour voir les types disponibles à transmettre)

Une fois terminé, vous trouverez le contenu de la réponse à l'intérieur avec HttpResponseMessage.Contentlequel vous pouvez consommer HttpContent.ReadAs*Async.

WDRust
la source
2
Ahhh merci pour le // here you can specify boundary if you need---^:)
sfarbota
1
pourquoi cela ne fonctionne pas? Tâche publique async <chaîne> 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 = "; attendre _client.PostAsync (url, requestContent); retourne "ok"; }
atapi19
1
asyncsur la première ligne et awaitsur la ligne avant la dernière ne sont pas nécessaires.
1valdis le
Pour les fichiers volumineux, ajoutez un contenu de flux à la demande plutôt qu'un tableau d'octets.
Elisabeth
1
@WDRust, avec un tableau d'octets, vous chargez d'abord le fichier entier en mémoire, puis vous l'envoyez. Avec un contenu de flux, le fichier est lu et envoyé à l'aide d'un tampon, ce qui est plus efficace en termes de mémoire.
Josef Bláha
53

Ceci est un exemple de la façon de publier une chaîne et un flux de fichiers avec HTTPClient à l'aide de MultipartFormDataContent. Le Content-Disposition et le Content-Type doivent être spécifiés pour chaque HTTPContent:

Voici mon exemple. J'espère que ça aide:

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
la source
11
@Trout Vous n'avez aucune idée de comment votre code m'a rendu si heureux aujourd'hui! +1
Pinch
6
C'est la réponse complète.
VK du
2
Je sais que nous ne sommes pas censés commenter une note de remerciement. Mais c'est ici le meilleur code que j'ai vu sur la façon de l'utiliser MultipartFormDataContent. Félicitations à vous monsieur
sebagomez
D'accord. C'est la seule réponse qui inclut la chaîne et le fichier json dans le contenu de la charge utile.
frostshoxx
Je teste sur mon ordinateur (win7 sp1, IIS 7.5) sans Content-Typeet Content-Dispositionc'est OK, mais sur le serveur 2008 R2 (IIS 7.5) je ne trouve pas de fichiers, c'est étrange. Alors je fais comme réponse.
chengzi
18

Voici un autre exemple d'utilisation HttpClientpour télécharger un fichier multipart/form-data.

Il télécharge un fichier vers une API REST et inclut le fichier lui-même (par exemple un JPG) et des paramètres API supplémentaires. Le fichier est directement téléchargé depuis le disque local via FileStream.

Voir ici pour l'exemple complet comprenant une logique supplémentaire spécifique à l'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
la source
12

Essayez cela, cela fonctionne pour moi.

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
la source
Sans défaut. Exactement ce que je recherchais dans un TestServer.CreatClient()scénario .NET Core d'un test d'intégration pour un téléchargement de données + fichier.
Vedran Mandić
si la méthode est HTTPGET comment passer formcontent
MBG
Les requêtes @MBG GET n'ont normalement pas de corps de requête par convention, vous ne pouvez donc pas télécharger un fichier à l'aide de GET (ou non, à moins que le serveur auquel vous envoyez soit très inhabituel - la plupart des serveurs Web ne s'y attendraient pas ou ne le prendraient pas en charge) , car il n'y a pas de corps de requête dans lequel inclure le fichier ou les données de formulaire qui l'accompagnent. Je crois que techniquement rien n'empêcherait cela en théorie, c'est juste que la convention dans presque toutes les implémentations de HTTP est que sémantiquement, GET est principalement destiné à récupérer des informations (plutôt qu'à envoyer) et n'a donc pas de corps
ADyson
9

Voici un échantillon complet qui a fonctionné pour moi. La boundaryvaleur de la demande est ajoutée automatiquement par .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
la source
Comment pouvons-nous envoyer un jeton avec ça? Voir ceci s'il vous plaît: stackoverflow.com/questions/48295877/…
@Softlion - J'ai du mal à ne PAS le charger en mémoire avant l'envoi. Si vous connaissez un meilleur moyen, veuillez poster ici: stackoverflow.com/questions/52446969/…
emery.noel
1

Exemple avec le préchargeur 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
la source
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
la source
Ce scénario est utilisé pour le téléchargement de fichiers dans le site API avec un certificat de sécurité
Rajenthiran T
0

J'ajoute un extrait de code qui montre comment publier un fichier dans une API qui a été exposée via le verbe DELETE http. Ce n'est pas un cas courant de télécharger un fichier avec le verbe DELETE http mais c'est autorisé. J'ai supposé l'authentification Windows NTLM pour autoriser l'appel.

Le problème auquel on pourrait faire face est que toutes les surcharges de HttpClient.DeleteAsyncméthode n'ont pas de paramètres pour HttpContentla façon dont nous l'obtenons dans PostAsyncmethod

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

Vous avez besoin des espaces de noms ci-dessous en haut de votre fichier C #:

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

PS Désolé pour tant de blocs utilisant (modèle IDisposable) dans mon code. Malheureusement, la syntaxe de l'utilisation de la construction de C # ne prend pas en charge l'initialisation de plusieurs variables dans une seule instruction.

RBT
la source
-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 l'éventreur
la source
Vous pouvez améliorer votre réponse en commentant le code que vous avez écrit
msrd0
OK msrd! Désolé pour mon novice. J'essaye de mettre un code clair comme "Erik Kalkoke", j'adore ça. Je partagerai mon code comme recevoir l'image par IFormFile au nœud de serveur 1 et passer au nœud de serveur 2 en augmentant du texte via la classe [MultipartFormDataContent] Oh! dernière ligne comme celle-ci. result = attendre res.Content.ReadAsStringAsync ();
Jack The Ripper