C # HttpClient 4.5 การอัปโหลดหลายส่วน / แบบฟอร์มข้อมูล


146

ไม่มีใครรู้วิธีใช้HttpClientin .Net 4.5 พร้อมmultipart/form-dataอัปโหลดไหม

ฉันไม่พบตัวอย่างใด ๆ บนอินเทอร์เน็ต


1
ฉันพยายาม แต่ฉันไม่รู้ว่าจะเริ่มอย่างไร .. โดยที่ฉันเพิ่ม byteArray ลงในเนื้อหาและอื่น ๆ ฉันต้องการความช่วยเหลือในการเริ่มต้น
ident

คุณสามารถดูคำตอบโพสต์นี้ (ด้วยการตั้งค่าพร็อกซี) stackoverflow.com/a/50462636/2123797
Ergin Çelik

คำตอบ:


156

ผลลัพธ์ของฉันมีลักษณะเช่นนี้:

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
ว้าวมันง่ายกว่ามากเมื่อทำการอัพโหลดไฟล์ขนาดใหญ่ไปยัง REST API ฉันไม่ชอบที่จะแสดงความคิดเห็นเพื่อขอบคุณ แต่ขอบคุณ มันพกพาได้สำหรับ Windows Phone 8
Léon Pelletier

1
รหัสนี้ล้มเหลวสำหรับฉันเนื่องจากสตริงขอบเขตส่งผ่านไปยังnew MultipartFormDataContent(...)มีอักขระขอบเขตไม่ถูกต้อง (อาจเป็นตัวคั่น "/") ไม่มีข้อผิดพลาดไม่มีไฟล์ที่โพสต์ในเซิร์ฟเวอร์ - ในกรณีของฉัน Context.Request.Files.Count = 0 ในตัวควบคุม API อาจเป็นเพียงNancyปัญหา แต่ฉันแนะนำให้ใช้บางสิ่งบางอย่างDateTime.Now.Ticks.ToString("x")แทน
Dunc

7
@MauricioAviles ลิงก์ของคุณเสีย ฉันพบสิ่งนี้ซึ่งอธิบายได้ดี: aspnetmonsters.com/2016/08/2016-08-27-httpclientwrong
Kevin Harker

1
หากคุณได้รับข้อผิดพลาด: " ไม่พบไฟล์ที่อัปโหลด " ลองเพิ่มkeyและfileNameพารามิเตอร์ไปที่content( bilddateiและupload.jpgในตัวอย่างนี้)
jhhwilliams

1
@KevinHarker อ่านลิงค์ที่สองอีกครั้ง ย่อหน้าที่พูดถึงการไม่ทิ้ง HttpClient อ้างอิงถึงการออกแบบก่อนหน้านี้ ง่ายที่จะสับสน โดยพื้นฐานแล้วด้วย IHttpClientFactory HttpClient Dispose ไม่ได้ทำอะไรเลย ( stackoverflow.com/a/54326424/476048 ) และตัวจัดการภายในได้รับการจัดการโดย HttpClientFactory
Berin Loritsch

83

มันทำงานได้มากกว่าหรือน้อยกว่าเช่นนี้ (ตัวอย่างเช่นการใช้ไฟล์รูปภาพ / 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ได้


2
Ahhh ขอบคุณสำหรับ// here you can specify boundary if you need---^:)
sfarbota

1
ทำไมถึงไม่ได้ผล? Public async Task <string> SendImage (byte [] foto) {var requestContent = ใหม่ MultipartFormDataContent (); var imageContent = ใหม่ ByteArrayContent (foto); imageContent.Headers.ContentType = MediaTypeHeaderValue.Parse ("image / jpeg"); requestContent.Add (imageContent, "foto", "foto.jpg"); string url = " myAddress / myWS / api / Home / SendImage? foto = "; คอย _client.PostAsync (url, requestContent); กลับ "ตกลง"; }
atapi19

1
asyncในบรรทัดแรกและawaitบนบรรทัดก่อนหน้าสุดท้ายไม่จำเป็น
1valdis

สำหรับไฟล์ขนาดใหญ่ให้เพิ่มเนื้อหาสตรีมลงในคำขอแทนที่จะเป็นอาร์เรย์ไบต์
Elisabeth

1
@WDRust ซึ่งมีอาร์เรย์ไบต์คุณจะโหลดไฟล์ทั้งหมดลงในหน่วยความจำก่อนแล้วจึงส่ง ด้วยเนื้อหาสตรีมไฟล์จะอ่านและส่งโดยใช้บัฟเฟอร์ซึ่งมีประสิทธิภาพมากขึ้นในแง่ของหน่วยความจำ
Josef Bláha

53

นี่เป็นตัวอย่างของวิธีการโพสต์สตริงและสตรีมไฟล์ด้วย 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();
        }
    }
}

11
@Trout คุณไม่รู้ว่ารหัสของคุณทำให้ฉันมีความสุขมาก ๆ วันนี้! +1
หยิก

6
นี่คือคำตอบที่สมบูรณ์
VK

2
ฉันรู้ว่าเราไม่ควรที่จะแสดงความคิดเห็นขอบคุณขอบคุณ MultipartFormDataContentแต่ตอนนี้ที่นี่คือรหัสที่ดีที่สุดที่ผมเคยเห็นเกี่ยวกับวิธีการใช้งาน ขอ
ชื่นชม

ตกลง นี่เป็นคำตอบเดียวที่มีสตริง json และไฟล์เป็นส่วนหนึ่งของเนื้อหา payload
frostshoxx

ฉันทดสอบบนคอมพิวเตอร์ของฉัน (win7 sp1, IIS 7.5) โดยไม่ต้องContent-TypeและContent-Dispositionก็โอเค แต่ใน Server 2008 R2 (IIS 7.5) ไม่สามารถค้นหาไฟล์มันแปลก ดังนั้นฉันทำตามคำตอบ
chengzi

18

นี่เป็นอีกตัวอย่างหนึ่งเกี่ยวกับวิธีการใช้ที่จะอัปโหลดHttpClientmultipart/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);
}

12

ลองใช้วิธีนี้เพื่อฉัน

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

ไม่มีที่ติ สิ่งที่ฉันกำลังค้นหาในTestServer.CreatClient()สถานการณ์. NET Core ของการทดสอบการรวมสำหรับการอัปโหลดข้อมูล + ไฟล์
Vedran Mandić

ถ้าวิธีนี้เป็น HTTPGET วิธีการส่งเนื้อหา
MBG

@MBG คำขอ GET ไม่มีการร้องขอตามปกติดังนั้นคุณจึงไม่สามารถอัปโหลดไฟล์โดยใช้ GET (หรือไม่เว้นแต่เซิร์ฟเวอร์ที่คุณส่งไปนั้นผิดปกติมาก - เว็บเซิร์ฟเวอร์ส่วนใหญ่ไม่คาดหวังหรือสนับสนุน) เนื่องจากไม่มีเนื้อหาคำขอที่จะรวมไฟล์หรือข้อมูลแบบฟอร์มประกอบ ผมเชื่อว่าในทางเทคนิคมีอะไรที่จะป้องกันไม่ให้มีการกระทำในทางทฤษฎีก็เพียงว่าการประชุมทั่วเกือบทุกการใช้งานของ HTTP ที่เป็นที่หมายได้รับเป็นหลักสำหรับการดึงข้อมูล (แทนที่จะส่ง) และอื่น ๆ ไม่ได้มีร่างกาย
ADyson

9

นี่คือตัวอย่างที่สมบูรณ์ที่ใช้งานได้สำหรับฉัน 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;

เราจะส่งโทเค็นด้วยสิ่งนี้ได้อย่างไร โปรดดูสิ่งนี้: stackoverflow.com/questions/48295877/…

@ Softlion - ฉันมีปัญหาไม่โหลดลงในหน่วยความจำก่อนที่จะส่ง หากคุณรู้วิธีที่ดีกว่าโปรดโพสต์ที่นี่: stackoverflow.com/questions/52446969/…
emery.noel

1

ตัวอย่างกับ 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();
    }
}

สถานการณ์นี้ใช้สำหรับการอัปโหลดไฟล์ไปยังไซต์ API พร้อมใบรับรองความปลอดภัย
Rajenthiran T

0

ฉันกำลังเพิ่มข้อมูลโค้ดซึ่งแสดงวิธีโพสต์ไฟล์ไปยัง API ซึ่งเปิดเผยผ่าน DELETE http verb นี่ไม่ใช่กรณีทั่วไปในการอัปโหลดไฟล์ที่มี DELETE http verb แต่อนุญาตให้ทำได้ ฉันถือว่า Windows NTLM รับรองความถูกต้องสำหรับการอนุญาตการโทร

ปัญหาที่หนึ่งอาจเผชิญคือ overloads ของ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);
    }
}

คุณต้องการ namespaces ด้านล่างที่ด้านบนของไฟล์ C # ของคุณ:

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

ป.ล. ขออภัยเกี่ยวกับบล็อกจำนวนมาก (รูปแบบ IDisposable) ในรหัสของฉัน ขออภัยไวยากรณ์ของการใช้โครงสร้างของ C # ไม่สนับสนุนการเริ่มต้นตัวแปรหลายตัวในคำสั่งเดียว


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

คุณสามารถปรับปรุงคำตอบของคุณโดยการแสดงความคิดเห็นในรหัสที่คุณเขียน
msrd0

ตกลง msrd! ขออภัยเกี่ยวกับสามเณรของฉัน ฉันพยายามใส่รหัสที่ชัดเจนเช่น "Erik Kalkoke" ฉันชอบมาก ฉันจะแบ่งปันรหัสของฉันเช่นรับภาพจาก IFormFile ที่เซิร์ฟเวอร์โหนด 1 และส่งไปยังเซิร์ฟเวอร์โหนด 2 โดยเพิ่มข้อความผ่านคลาส [MultipartFormDataContent] โอ้! บรรทัดสุดท้ายเช่นนี้ ผล = รอ res.Content.ReadAsStringAsync ();
แจ็คเดอะริปเปอร์
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.