private static Stream GetMultipartFormPre(SendFileInfo file, long filelength, string boundry) { var result = new MemoryStream(1000); using (var writer = new StreamWriter(result, UTF8, 16, true)) { if (file.Parameters != null) { foreach (var pair in file.Parameters) { writer.Write($"--{boundry}\r\n"); writer.Write($"Content-Disposition: form-data; name=\"{pair.Key}\"\r\n\r\n{pair.Value}\r\n"); } } writer.Write($"--{boundry}\r\n"); writer.Write($"Content-Disposition: form-data; name=\"{file.FormName}\"; filename={file.FileName}\r\n"); writer.Write($"Content-Type: application/octet-stream\r\n"); writer.Write($"Content-Length: {filelength}\r\n\r\n"); } result.Position = 0; return(result); }
private static async Task CopyStreams(Stream source, Stream destination, SendFileInfo info, CopyStreamState state) { var buffer = new byte[info.BufferSize]; int bytesRead; var lastProgessCalled = false; while ((bytesRead = await source.ReadAsync(buffer, 0, buffer.Length, info.CancellationToken)) > 0) { await destination.WriteAsync(buffer, 0, bytesRead, info.CancellationToken); lastProgessCalled = false; if (state != null) { state.Pos += bytesRead; if (info.Progress != null && (state.Pos >= state.NextPos)) { state.NextPos = await info.Progress.Invoke(state.Pos); lastProgessCalled = true; } } } if (state != null && info.Progress != null && !lastProgessCalled) { await info.Progress.Invoke(state.Pos); } }
/// <summary> /// Uploads file /// </summary> /// <typeparam name="R">Result type</typeparam> /// <param name="method">HTTP method</param> /// <param name="url">URL for request</param> /// <param name="file">File upload parameters. Input stream must support Length</param> /// <returns>Async result object</returns> public async Task <R> SendFile <R>(HttpMethod method, string url, SendFileInfo file) { R result = default(R); await Retry.Do( RetryTimes, RetryDelay, async() => { var client = await GetHttpClient(url).ConfigureAwait(false); try { client.Method = method.ToString(); client.AllowWriteStreamBuffering = false; var boundry = Guid.NewGuid().ToString(); client.ContentType = $"multipart/form-data; boundary={boundry}"; client.SendChunked = true; using (var input = file.StreamOpener()) { var pre = GetMultipartFormPre(file, input.Length, boundry); var post = GetMultipartFormPost(boundry); client.ContentLength = pre.Length + input.Length + post.Length; using (var output = await client.GetRequestStreamAsync().ConfigureAwait(false)) { var state = new CopyStreamState(); await CopyStreams(pre, output, file, state).ConfigureAwait(false); await CopyStreams(input, output, file, state).ConfigureAwait(false); await CopyStreams(post, output, file, state).ConfigureAwait(false); } } using (var response = (HttpWebResponse)await client.GetResponseAsync().ConfigureAwait(false)) { if (!response.IsSuccessStatusCode()) { return(await LogBadResponse(response).ConfigureAwait(false)); } result = await response.ReadAsAsync <R>().ConfigureAwait(false); } return(true); } catch (Exception) { client.Abort(); throw; } }, GeneralExceptionProcessor).ConfigureAwait(false); return(result); }
private static async Task CopyStreams(Stream source, Stream destination, SendFileInfo info, CopyStreamState state) { var buffer = new byte[info.BufferSize]; int bytesRead; while ((bytesRead = await source.ReadAsync(buffer, 0, buffer.Length).ConfigureAwait(false)) > 0) { if (info.CancellationToken != null && info.CancellationToken.Value.IsCancellationRequested) { throw new TaskCanceledException(); } await destination.WriteAsync(buffer, 0, bytesRead).ConfigureAwait(false); state.Pos += bytesRead; if (info.Progress != null && state.Pos >= state.NextPos) { state.NextPos = info.Progress.Invoke(state.Pos); } } }
/// <summary> /// Uploads file /// </summary> /// <typeparam name="R">Result type</typeparam> /// <param name="method">HTTP method</param> /// <param name="url">URL for request</param> /// <param name="file">File upload parameters. Input stream must support Length</param> /// <returns>Async result object</returns> public async Task <R> SendFile <R>(HttpMethod method, string url, SendFileInfo file) { R result = default(R); await Retry.Do( RetryTimes, RetryDelay, async() => { var client = await GetHttpClient(url).ConfigureAwait(false); try { client.Method = method.ToString(); client.AllowWriteStreamBuffering = false; string boundry = Guid.NewGuid().ToString(); client.ContentType = $"multipart/form-data; boundary={boundry}"; client.SendChunked = true; using (var input = file.StreamOpener()) { var pre = GetMultipartFormPre(file, input.Length, boundry); var post = GetMultipartFormPost(boundry); client.ContentLength = pre.Length + input.Length + post.Length; using (var output = await client.GetRequestStreamAsync().ConfigureAwait(false)) { if (file.CancellationToken != null) { var token = (CancellationToken)file.CancellationToken; await pre.CopyToAsync(output, 81920).ConfigureAwait(false); //await input.CopyToAsync(output, 81920).ConfigureAwait(false); await CopyToStreamAsync(input, output, 81920, token, file.Progress).ConfigureAwait(false); if (token.IsCancellationRequested) { client.Abort(); throw new OperationCanceledException(token); } await post.CopyToAsync(output, 81920).ConfigureAwait(false); } else { await pre.CopyToAsync(output).ConfigureAwait(false); await input.CopyToAsync(output).ConfigureAwait(false); await post.CopyToAsync(output).ConfigureAwait(false); } } } using (var response = (HttpWebResponse)await client.GetResponseAsync().ConfigureAwait(false)) { if (!response.IsSuccessStatusCode()) { return(await LogBadResponse(response).ConfigureAwait(false)); } result = await response.ReadAsAsync <R>().ConfigureAwait(false); } return(true); } catch (Exception) { client.Abort(); throw; } }, GeneralExceptionProcessor).ConfigureAwait(false); return(result); }