Как вывести на экран результат отправки файлов в процентах С#?
Я отправляю файлы по http запросу, в моем методе SendFile(). Туда я передаю ссылку куда отправлять файлы и сами файлы. Создаю объект типа HttpClient();. Создаю HttpRequestMessage() в который помещаю свой content. На сервере настроена авторизация пользователей и каждому дается токен, поэтому в заголовках к запросу я так же указываю этот токен. Когда полностью запрос сформирован я вызываю метод client.SendAsync().Result. В зависимости от размера файла этот метод с течением определенного времени возвращает ответ. Но я не могу отследить этот момент, что бы вывести информацию для пользователя, о процессе отправки файла. Вот код метода:
private void _SendFile(string ShortUrl, ObservableCollection<LastFilesModel> files)
{
try
{
//Полная ссылка
var url = IpAddress_server + ShortUrl;
client = new HttpClient();
var content = new MultipartFormDataContent();
content.Headers.ContentType.MediaType = "multipart/form-data";
int count = 0;
string key = "";
foreach(var file in files)
{
count++;
var stream = new System.IO.FileStream(file.FullName, System.IO.FileMode.Open, System.IO.FileAccess.Read);
key = "file_" + count;
content.Add(new StreamContent(stream), key, file.ShortName);
}
// Тип Запроса
var httpMethod = HttpMethod.Post;
// Добавление данных в запрос
var request = new HttpRequestMessage()
{
RequestUri = new Uri(url),
Method = httpMethod,
Content = content,
};
// Токен который юзер получит при авторизации на сервере
request.Headers.Add("Authorization", "Bearer " + MyUser.Token);
// Отправка и ожидание ответа
var response = client.SendAsync(request).Result;
response.EnsureSuccessStatusCode();
}
catch(Exception ex)
{
var s =ex.Message;
}
}
Когда срабатывает метод client.SendAsync() то переменная response будет ожидать ответа, программа не зависает, просто спустя какое то время эта переменная будет содержать ответ в виде Json строки. Помогите понять как вместо ожидания ответа сделать прогресс отправки, в виде процентов или подобного.
Команда:
public ICommand SendMessage
{
get
{
return new Command(async (object obj) =>
{
//.... там выше код который отправлял сообщение на данный момент он лишний.
if(AttachFileCollection.Count != 0)
{
// Пользователь
var user = await UserDbService.GetUser();
// отправка файла
SendFile("/api/message/upload/chatroom/"+Room_id+"/user/"+ user.Id, AttachFileCollection);
// Настройки на экране
FullSize = 0;
IsVisibleAttachFile = false;
AttachFileCollection.Clear();
}
});
}
} // SendMessage
Вызов приватного метода
public void SendFile(string ShortUrl, ObservableCollection<LastFilesModel> files)
{
_SendFile(ShortUrl, files);
}
Ответы (1 шт):
Для отчетов о загрузке файлов на сервер я создал такой HttpContent.
(код не проверял, писал на коленке)
public class ProgessFileStreamContent : HttpContent
{
private const int bufferSize = 8192;
private readonly string _path;
private readonly Action<int> _callback;
private readonly long _length;
private readonly CancellationTokenSource _cts;
public ProgessFileStreamContent(string path, Action<int> callback)
{
_path = path;
_callback = callback;
_length = new FileInfo(path).Length;
_cts = new CancellationTokenSource();
}
protected override async Task SerializeToStreamAsync(Stream stream, TransportContext context)
{
if (_cts.IsCancellationRequested)
throw new ObjectDisposedException(nameof(ProgessFileStreamContent));
long position = 0;
int oldProgress = -1;
byte[] buffer = new byte[bufferSize];
using var fs = File.OpenRead(_path);
int bytesRead = 0;
while ((bytesRead = await fs.ReadAsync(buffer, 0, buffer.Length, _cts.Token)) > 0)
{
await stream.WriteAsync(buffer, 0, bytesRead, _cts.Token);
position += bytesRead;
int progress = (int)(position * 100 / _length);
if (progress != oldProgress)
{
oldProgress = progress;
_callback(progress);
}
}
}
protected override void Dispose(bool disposing)
{
if (disposing)
{
_cts.Cancel();
_cts.Dispose();
}
base.Dispose(disposing);
}
protected override bool TryComputeLength(out long length)
{
length = _length;
return true;
}
}
Чтобы показать, как его использовать, написал вот такой класс
public class LastFilesModel
{
public string ShortName { get; set; }
public string FullName { get; set; }
}
public class HttpUploader
{
private static readonly HttpClient client = new HttpClient();
public string IpAddress_server { get; set; }
public async Task<string> UploadFilesAsync(string shortUrl, IEnumerable<LastFilesModel> pathNames, IProgress<int> totalProgress)
{
LastFilesModel[] files = pathNames.ToArray();
string url = IpAddress_server + shortUrl;
using var content = new MultipartFormDataContent();
int[] progressData = new int[files.Length];
int oldProgress = -1;
for (int i = 0; i < files.Length; i++)
{
int index = i;
Action<int> progress = x =>
{
progressData[index] = x;
int p = progressData.Sum() / progressData.Length;
if (oldProgress != p)
{
oldProgress = p;
totalProgress.Report(p);
}
};
LastFilesModel file = files[i];
content.Add(new ProgessFileStreamContent(file.FullName, progress), $"file_{i + 1}", file.ShortName);
}
using var request = new HttpRequestMessage(HttpMethod.Post, url) { Content = content };
using var response = await client.SendAsync(request, HttpCompletionOption.ResponseHeadersRead).ConfigureAwait(false);
return await response.EnsureSuccessStatusCode().Content.ReadAsStringAsync().ConfigureAwait(false);
}
}
Не обрабатывайте исключения в глубинах логики, когда надо прервать выполнение при сбоях. Делайте это на верхнем уровне.
Команду кстати при каждом вызове инициализируете, немного переделаю
private readonly _uploader = new HttpUploader();
public ICommand SendMessage { get; } = new Command(async (object obj) =>
{
try
{
//....
if (AttachFileCollection.Count != 0)
{
var user = await UserDbService.GetUser();
IProgress<int> progress = new Progress<int>(UpdateProgress);
await _uploader.UploadFilesAsync("/api/message/upload/chatroom/"+Room_id+"/user/"+ user.Id, AttachFileCollection, progress);
// Настройки на экране
FullSize = 0;
IsVisibleAttachFile = false;
AttachFileCollection.Clear();
}
}
catch (Exception ex)
{
// ...ex.Message
}
});
private void UpdateProgress(int progress)
{
// здесь обновляйте UI
}
Progress<T> имеет особенность, при которой его делегат всегда будет вызван в том же контексте синхронизации, в котором он сам был создан, в данном случае - в UI потоке.