wpf 上传文件带进度条
在网上找了好久,才找到一位俄罗斯大神写的文章。
遇到超大文件的话还是会报异常。
服务端采用webapi
[Authorize] [HttpPost] public async Task<IActionResult> Post(IFormFile file) { var content = new MultipartFormDataContent(); var fileContent = new StreamContent(file.OpenReadStream()); fileContent.Headers.ContentType = MediaTypeHeaderValue.Parse(file.ContentType); Stream stream = file.OpenReadStream(); return Ok(); }
客户端wpf
public async Task UploadAsync(Uri uri, Stream stream, IProgress<float> progress, CancellationToken cancelToken = default(CancellationToken)) { if (uri == null) throw new ArgumentNullException(nameof(uri)); if (stream == null) throw new ArgumentNullException(nameof(stream)); if (progress == null) throw new ArgumentNullException(nameof(progress)); // Название файла var fileName = System.IO.Path.GetFileName(((FileStream)stream).Name); // Подготавливаем контент потока var streamContent = new ProgressStreamContent(stream); streamContent.Headers.ContentType = new MediaTypeHeaderValue("text/plain"); streamContent.ProgressChanged += (bytes, currBytes, totalBytes) => progress.Report((float)currBytes / totalBytes * 100); // Данные на отправление var content = new MultipartFormDataContent(); content.Add(streamContent, "file", fileName); try { using (var client = new HttpClient()) { client.DefaultRequestHeaders.Accept.Clear(); client.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", token); // Здесь скорее всего устанавливаем всякие заголовки // ... // Посылаем Post запрос var response = await client.PostAsync(uri, content, cancelToken); // Проверяем статус ответа switch (response.StatusCode) { case HttpStatusCode.OK: this.Close(); break; default: break; /* Здесь проверяем статус ответа и предпринимаем действия */ } } } catch (HttpRequestException ex) { throw new Exception(ex.Message); } }
public delegate void ProgressHandler(long bytes, long currentBytes, long totalBytes); class ProgressStreamContent : StreamContent { private const int DEFAULT_BUFFER_SIZE = 4096; public event ProgressHandler ProgressChanged = delegate { }; private long currentBytes = 0; private long totalBytes = -1; public Stream InnerStream { get; } public int BufferSize { get; } public ProgressStreamContent(Stream innerStream, int bufferSize = DEFAULT_BUFFER_SIZE) : base(innerStream, bufferSize) { InnerStream = innerStream ?? throw new ArgumentNullException(nameof(innerStream)); BufferSize = bufferSize > 0 ? bufferSize : throw new ArgumentOutOfRangeException(nameof(bufferSize)); } private void ResetInnerStream() { if (InnerStream.Position != 0) { // Если внутренний поток нужно считать повторно, то этот внутренний поток должен поддерживать // возврат каретки(например FileStream), иначе внутренний поток не может быть считан повторно // в целевой поток(например NetworkStream) if (InnerStream.CanSeek) { InnerStream.Position = 0; currentBytes = 0; } else throw new InvalidOperationException("The inner stream has already been read!"); } } protected override async Task SerializeToStreamAsync(Stream stream, TransportContext context) { if (stream == null) throw new ArgumentNullException(nameof(stream)); // Сбрасываем состояние внутреннего потока ResetInnerStream(); // Если общее количество байт еще не получено, то пытаемся получить // его из заголовков контента if (totalBytes == -1) totalBytes = Headers.ContentLength ?? -1; // Если общее количество байт еще не найдено, то пытаемся // вычислить его из потока if (totalBytes == -1 && TryComputeLength(out var computedLength)) totalBytes = computedLength == 0 ? -1 : computedLength; // Если общее количество байт отрицательное значение, то // присваеваем ему -1, идентифицирующее о невалидном общем количестве байт totalBytes = Math.Max(-1, totalBytes); // Начинаем читать внутренний поток var buffer = new byte[BufferSize]; var bytesRead = 0; while ((bytesRead = await InnerStream.ReadAsync(buffer, 0, buffer.Length)) != 0) { stream.Write(buffer, 0, bytesRead); currentBytes += bytesRead; Thread.Sleep(100); // Генерируем событие ProgressChanged, чтобы оповестить о текущем прогрессе считывания ProgressChanged(bytesRead, currentBytes, totalBytes); } } protected override bool TryComputeLength(out long length) { var result = base.TryComputeLength(out length); totalBytes = length; return result; } protected override void Dispose(bool disposing) { if (disposing) InnerStream.Dispose(); base.Dispose(disposing); } }
调用
await UploadAsync(new Uri(upUrl + apiName), stream, new Progress<float>((percent) => { this.Pbupload.Dispatcher.Invoke(new Action(delegate { Pbupload.Value = percent; })); }));