问题
A am creating a file upload app for Android and iOS using Xamarin PCL and i have managed to implement file upload and some sort of progress bar, but it is not working properly.
I saw some answers on stack overflow for displaying download progress, but i want to notify my users about upload progress and did not find any solution.
Here is my code:
public static async Task<string> PostFileAsync (Stream filestream, string filename, int filesize) {
var progress = new System.Net.Http.Handlers.ProgressMessageHandler ();
//Progress tracking
progress.HttpSendProgress += (object sender, System.Net.Http.Handlers.HttpProgressEventArgs e) => {
int progressPercentage = (int)(e.BytesTransferred*100/filesize);
//Raise an event that is used to update the UI
UploadProgressMade(sender, new System.Net.Http.Handlers.HttpProgressEventArgs(progressPercentage, null, e.BytesTransferred, null));
};
using (var client = HttpClientFactory.Create(progress)) {
using (var content = new MultipartFormDataContent ("------" + DateTime.Now.Ticks.ToString ("x"))) {
content.Add (new StreamContent (filestream), "Filedata", filename);
using (var message = await client.PostAsync ("http://MyUrl.example", content)) {
var result = await message.Content.ReadAsStringAsync ();
System.Diagnostics.Debug.WriteLine ("Upload done");
return result;
}
}
}
}
Some sort of progress is displayed, but when the progress reaches 100%, the file is not uploaded yet. Message "Upload done" is also printed some time after i have received the last progress message.
Maybe the progress is displaying bytes sent out of the device and not already uploaded bytes, so when it says, that it is 100%, all of the bytes are just sent out but not yet received by the server?
Edit: Tried this solution: https://forums.xamarin.com/discussion/56716/plans-to-add-webclient-to-pcl and it works a bit better.
回答1:
Try something like this:
I faced same issue. I fixed it by implementing custom HttpContent
. I use this object to track percentage of upload progress, you can add an event to and listen it. You should customize SerializeToStreamAsync
method.
internal class ProgressableStreamContent : HttpContent
{
private const int defaultBufferSize = 4096;
private Stream content;
private int bufferSize;
private bool contentConsumed;
private Download downloader;
public ProgressableStreamContent(Stream content, Download downloader) : this(content, defaultBufferSize, downloader) {}
public ProgressableStreamContent(Stream content, int bufferSize, Download downloader)
{
if(content == null)
{
throw new ArgumentNullException("content");
}
if(bufferSize <= 0)
{
throw new ArgumentOutOfRangeException("bufferSize");
}
this.content = content;
this.bufferSize = bufferSize;
this.downloader = downloader;
}
protected override Task SerializeToStreamAsync(Stream stream, TransportContext context)
{
Contract.Assert(stream != null);
PrepareContent();
return Task.Run(() =>
{
var buffer = new Byte[this.bufferSize];
var size = content.Length;
var uploaded = 0;
downloader.ChangeState(DownloadState.PendingUpload);
using(content) while(true)
{
var length = content.Read(buffer, 0, buffer.Length);
if(length <= 0) break;
downloader.Uploaded = uploaded += length;
stream.Write(buffer, 0, length);
downloader.ChangeState(DownloadState.Uploading);
}
downloader.ChangeState(DownloadState.PendingResponse);
});
}
protected override bool TryComputeLength(out long length)
{
length = content.Length;
return true;
}
protected override void Dispose(bool disposing)
{
if(disposing)
{
content.Dispose();
}
base.Dispose(disposing);
}
private void PrepareContent()
{
if(contentConsumed)
{
// If the content needs to be written to a target stream a 2nd time, then the stream must support
// seeking (e.g. a FileStream), otherwise the stream can't be copied a second time to a target
// stream (e.g. a NetworkStream).
if(content.CanSeek)
{
content.Position = 0;
}
else
{
throw new InvalidOperationException("SR.net_http_content_stream_already_read");
}
}
contentConsumed = true;
}
}
Refer :
- https://github.com/paulcbetts/ModernHttpClient/issues/80
- Progress bar for HttpClient uploading
- https://forums.xamarin.com/discussion/18649/best-practice-to-upload-image-selected-to-a-web-api
回答2:
This is because you are doing the math wrong.
Change : int progressPercentage = (int)(e.BytesTransferred*100/filesize);
To : int progressPercentage = (int)(e.BytesTransferred/filesize) *100;
use this code instead:
double bytesOut = double.Parse(e.BytesTransferred.ToString());
double totalBytes = double.Parse(filesize.ToString());
double percentage = bytesOut / totalBytes * 100;
or you can simply use e.ProgressPercentage
来源:https://stackoverflow.com/questions/35320238/how-to-display-upload-progress-using-c-sharp-httpclient-postasync