C#:HttpClient,将多个文件作为MultipartFormDataContent上传时的文件上传进度
Posted
技术标签:
【中文标题】C#:HttpClient,将多个文件作为MultipartFormDataContent上传时的文件上传进度【英文标题】:C#: HttpClient, File upload progress when uploading multiple file as MultipartFormDataContent 【发布时间】:2016-12-29 11:07:02 【问题描述】:我正在使用此代码上传多个文件,并且运行良好。它使用现代的httpclient库。
public async Task<string> PostImages (int platform, string url, List<byte []> imageList)
try
int count = 1;
var requestContent = new MultipartFormDataContent ();
foreach (var image in imageList)
var imageContent = new ByteArrayContent (image);
imageContent.Headers.ContentType = MediaTypeHeaderValue.Parse ("image/jpeg");
requestContent.Add (imageContent, "image" + count, "image.jpg");
count++;
var cookieHandler = new NativeCookieHandler ();
var messageHandler = new NativeMessageHandler (false, false, cookieHandler);
cookieHandler.SetCookies (cookies);
using (var client = new HttpClient (messageHandler))
client.DefaultRequestHeaders.TryAddWithoutValidation ("User-Agent", GetUserAgent (platform));
using (var r = await client.PostAsync (url, requestContent))
string result = await r.Content.ReadAsStringAsync ();
System.Diagnostics.Debug.WriteLine ("PostAsync: " + result);
return result;
catch (Exception e)
System.Diagnostics.Debug.WriteLine (e.Message);
return null;
现在我需要上传文件时的进度。我在谷歌搜索,发现我需要使用 ProgressStreamContent
https://github.com/paulcbetts/ModernHttpClient/issues/80
由于 ProgressStreamContent 包含一个接受流的构造函数,因此我将 MultipartFormDataContent 转换为流并在其构造函数中使用它。但是,它不起作用。上传失败。我认为这是因为它是所有文件的流,这不是我的后端所期望的。
public async Task<string> PostImages (int platform, string url, List<byte []> imageList)
try
int count = 1;
var requestContent = new MultipartFormDataContent ();
// here you can specify boundary if you need---^
foreach (var image in imageList)
var imageContent = new ByteArrayContent (image);
imageContent.Headers.ContentType = MediaTypeHeaderValue.Parse ("image/jpeg");
requestContent.Add (imageContent, "image" + count, "image.jpg");
count++;
var cookieHandler = new NativeCookieHandler ();
var messageHandler = new NativeMessageHandler (false, false, cookieHandler);
cookieHandler.SetCookies (RestApiPaths.cookies);
var stream = await requestContent.ReadAsStreamAsync ();
var client = new HttpClient (messageHandler);
client.DefaultRequestHeaders.TryAddWithoutValidation ("User-Agent", RestApiPaths.GetUserAgent (platform));
var request = new HttpRequestMessage (HttpMethod.Post, url);
var progressContent = new ProgressStreamContent (stream, 4096);
progressContent.Progress = (bytes, totalBytes, totalBytesExpected) =>
Console.WriteLine ("Uploading 0/1", totalBytes, totalBytesExpected);
;
request.Content = progressContent;
var response = await client.SendAsync (request);
string result = await response.Content.ReadAsStringAsync ();
System.Diagnostics.Debug.WriteLine ("PostAsync: " + result);
return result;
catch (Exception e)
System.Diagnostics.Debug.WriteLine (e.Message);
return null;
我应该怎么做才能让它工作?任何帮助表示赞赏
【问题讨论】:
你必须使用 MultiPartContent,而不是 ByteArrayContent 我将文件作为字节数组。 服务器不接受字节数组,http服务器大多需要多部分形式编码的内容,这是服务器期望数据的方式,而不是您发送的方式 我将每个 ByteArrayContent 添加到 MultipartFormDataContent。请查看代码,它就像我在文章开头所说的那样工作。 【参考方案1】:我有一个 ProgressableStreamContent 的工作版本。请注意,我在构造函数中添加了标题,这是原始 ProgressStreamContent 中的一个错误,它没有添加标题!
internal class ProgressableStreamContent : HttpContent
/// <summary>
/// Lets keep buffer of 20kb
/// </summary>
private const int defaultBufferSize = 5*4096;
private HttpContent content;
private int bufferSize;
//private bool contentConsumed;
private Action<long,long> progress;
public ProgressableStreamContent(HttpContent content, Action<long,long> progress) : this(content, defaultBufferSize, progress)
public ProgressableStreamContent(HttpContent content, int bufferSize, Action<long,long> progress)
if (content == null)
throw new ArgumentNullException("content");
if (bufferSize <= 0)
throw new ArgumentOutOfRangeException("bufferSize");
this.content = content;
this.bufferSize = bufferSize;
this.progress = progress;
foreach (var h in content.Headers)
this.Headers.Add(h.Key,h.Value);
protected override Task SerializeToStreamAsync(Stream stream, TransportContext context)
return Task.Run(async () =>
var buffer = new Byte[this.bufferSize];
long size;
TryComputeLength(out size);
var uploaded = 0;
using (var sinput = await content.ReadAsStreamAsync())
while (true)
var length = sinput.Read(buffer, 0, buffer.Length);
if (length <= 0) break;
//downloader.Uploaded = uploaded += length;
uploaded += length;
progress?.Invoke(uploaded, size);
//System.Diagnostics.Debug.WriteLine($"Bytes sent uploaded of size");
stream.Write(buffer, 0, length);
stream.Flush();
stream.Flush();
);
protected override bool TryComputeLength(out long length)
length = content.Headers.ContentLength.GetValueOrDefault();
return true;
protected override void Dispose(bool disposing)
if (disposing)
content.Dispose();
base.Dispose(disposing);
另请注意,它需要 HttpContent,而不是流。
这就是你可以使用它的方式。
var progressContent = new ProgressableStreamContent (
requestContent,
4096,
(sent,total) =>
Console.WriteLine ("Uploading 0/1", sent, total);
);
【讨论】:
这不起作用(不再)。它将通过“SerializeToStreamAsync”中的整个请求,然后才开始使用带宽并实际上传它。至少在 Xamarin 中。 可能是,但是在底层的http客户端实现中,你使用的是哪个实现? 对不起,我正在度假。我正在使用 Xamarin Forms (Mono) System.Net.Http 我在 Xamarin android 上使用基于 OkHttp 的 HttpClient,它运行良好。 好的。 ios 呢?【参考方案2】:最简单的方法
您可以通过跟踪您要上传的文件的 FileStream 的Position
来获得准确的进度。
FileStream fileToUpload = File.OpenRead(@"C:\test.mp3");
HttpContent content = new StreamContent(fileToUpload);
HttpRequestMessage msg = new HttpRequestMessage
Content=content,
RequestUri = new Uri(--yourUploadURL--)
bool keepTracking = true; //to start and stop the tracking thread
new Task(new Action(() => progressTracker(fileToUpload, ref keepTracking); )).Start();
var result = httpClient.SendAsync(msg).Result;
keepTracking = false; //stops the tracking thread
而progressTracker()
函数定义为
void progressTracker(FileStream streamToTrack, ref bool keepTracking)
int prevPos = -1;
while (keepTracking)
int pos = (int)Math.Round(100 * (streamToTrack.Position / (double)streamToTrack.Length));
if (pos != prevPos)
Console.WriteLine(pos + "%");
prevPos = pos;
Thread.Sleep(100);
【讨论】:
以上是关于C#:HttpClient,将多个文件作为MultipartFormDataContent上传时的文件上传进度的主要内容,如果未能解决你的问题,请参考以下文章
在 c# HttpClient 4.5 中发布 multipart/form-data
C# Xamarin 文件上传到 API 可以使用 RestSharp,但不能使用 HttpClient
使用 C# .NET HTTPClient 在 Node JS 服务器上通过 Multer 将 Revit 文件上传到 Autodesk Forge OSS
在 Kotlin 多平台项目中使用 Ktor HttpClient 将文件作为二进制文件