private async Task CopyToStreamWithProgress(Stream source, Stream destination, ProgressSocketSessionService sessionService, WebsocketSession targetSession, int totalBytes = -1, string stepId = "", CancellationToken cancellationToken = default(CancellationToken), int bufferSize = 0x1000) { byte[] buffer = new byte[bufferSize]; int currentBytesRead; int totalBytesRead = 0; while ((currentBytesRead = await source.ReadAsync(buffer, 0, buffer.Length, cancellationToken)) > 0) { bool shouldAbort = targetSession.GetAttributeValue <bool>("abort"); if (shouldAbort) { break; } await destination.WriteAsync(buffer, 0, currentBytesRead, cancellationToken); cancellationToken.ThrowIfCancellationRequested(); totalBytesRead += currentBytesRead; sessionService.UpdateProgress(targetSession.SessionKey, totalBytesRead, totalBytes, stepId); //await Task.Delay(1); // Stops worker threads from going crazy with CPU } }
public async Task <Models.FileMap> StreamFileToDiskWithProgress(HttpRequest request, ModelStateDictionary modelState, ILogger logger, ProgressSocketSessionService sessionService, string sessionKey) { if (!modelState.IsValid) { return(null); } if (!IsMultipartContentType(request.ContentType)) { modelState.AddModelError("File", "Form content type must be 'multipart'"); return(null); } var boundary = GetBoundary(MediaTypeHeaderValue.Parse(request.ContentType), sessionService.DefaultFormOptions.MultipartBoundaryLengthLimit); var reader = new MultipartReader(boundary, request.Body); var section = await reader.ReadNextSectionAsync(); string filenameForDisplay = ""; string filenameOnDisk = ""; string fileContentType = ""; WebsocketSession targetSession = sessionService.GetSessionByKey(sessionKey); if (targetSession == null) { modelState.AddModelError("File", "The requested session could not be found."); return(null); } targetSession.SetAttribute("abort", false); int totalBytes = targetSession.GetAttributeValue <int>("unitTotal"); if (section == null) { sessionService.UpdateProgress(targetSession.SessionKey, -1, -1, ""); } while (section != null) { var hasContentDispositionHeader = ContentDispositionHeaderValue.TryParse(section.ContentDisposition, out var contentDisposition); if (hasContentDispositionHeader) { // This check assumes that there's a file // present without form data. If form data // is present, this method immediately fails // and returns the model error. if (!HasFileContentDisposition(contentDisposition)) { modelState.AddModelError("File", $"The request couldn't be processed (Error 2)."); return(null); } else { // Don't trust the file name sent by the client. To display // the file name, HTML-encode the value. filenameForDisplay = WebUtility.HtmlEncode(contentDisposition.FileName.Value); filenameOnDisk = Path.GetRandomFileName(); fileContentType = GetStaticFileContentType(contentDisposition.FileName.Value); if (!Directory.Exists(sessionService.QuarantinedFilePath)) { Directory.CreateDirectory(sessionService.QuarantinedFilePath); } string quarantinedPath = Path.Combine(sessionService.QuarantinedFilePath, filenameOnDisk); await SaveFileToDiskWithProgress(section, contentDisposition, modelState, quarantinedPath, sessionService.PermittedExtensions, sessionService.FileSizeLimit, sessionService, targetSession, totalBytes); if (!modelState.IsValid) { return(null); } if (!Directory.Exists(sessionService.FileStoragePath)) { Directory.CreateDirectory(sessionService.FileStoragePath); } System.IO.File.Move(quarantinedPath, Path.Combine(sessionService.FileStoragePath, filenameOnDisk)); logger.LogInformation("Uploaded file '{TrustedFileNameForDisplay}' saved to '{TargetFilePath}' as {TrustedFileNameForFileStorage}", filenameForDisplay, sessionService.FileStoragePath, filenameOnDisk); } } // Drain any remaining section body that hasn't been consumed and // read the headers for the next section. section = await reader.ReadNextSectionAsync(); } return(new Models.FileMap() { FilenameForDisplay = filenameForDisplay, FilenameOnDisk = filenameOnDisk, ContentType = fileContentType }); }