public static async Task <KeyValuePair <string, string> > HandleFormDispositionSection(MultipartSection section, ContentDispositionHeaderValue contentDisposition)
        {
            FormMultipartSection formDataSection = new FormMultipartSection(section, contentDisposition);

            // Content-Disposition: form-data; name="key"
            //
            // value

            // Do not limit the key name length here because the multipart headers length limit is already in effect.
            string key   = formDataSection.Name;
            string value = await formDataSection.GetValueAsync();

            return(new KeyValuePair <string, string>(key, value));
        }
        /// <summary>
        /// Parses the HttpRequest and extracts the file sections and the form section
        /// Sets the _formSection and _fileSections fields.
        /// </summary>
        /// <returns></returns>
        private static async Task <(FormMultipartSection formSection, List <FileMultipartSection> fileSections)> ExtractSections(HttpRequest request, bool fileOnly = false)
        {
            if (!MultipartRequestHelper.IsMultipartContentType(request.ContentType))
            {
                throw new Exception($"Expected a multipart request, but got {request.ContentType}");
            }

            FormMultipartSection        formSection  = null;
            List <FileMultipartSection> fileSections = new List <FileMultipartSection>();

            string boundary = MultipartRequestHelper.GetBoundary(request);
            var    reader   = new MultipartReader(boundary, request.Body);

            MultipartSection section = await reader.ReadNextSectionAsync();

            while (section != null)
            {
                ContentDispositionHeaderValue contentDisposition;
                var hasContentDispositionHeader = ContentDispositionHeaderValue.TryParse(section.ContentDisposition, out contentDisposition);

                if (hasContentDispositionHeader)
                {
                    if (MultipartRequestHelper.HasFileContentDisposition(contentDisposition))
                    {
                        fileSections.Add(section.AsFileSection());
                    }
                    else if (MultipartRequestHelper.HasFormDataContentDisposition(contentDisposition))
                    {
                        formSection = section.AsFormDataSection();
                    }
                }

                if (fileOnly && fileSections.Count > 0)
                {
                    break;
                }

                // Drains any remaining section body that has not been consumed and
                // reads the headers for the next section.
                section = await reader.ReadNextSectionAsync();
            }

            return(formSection, fileSections);
        }
        /// <summary>
        /// Parses the form body into a FormValueProvider
        /// </summary>
        /// <param name="formSection"></param>
        /// <returns></returns>
        private static async Task <FormValueProvider> ParseFormBody(FormMultipartSection formSection)
        {
            // Used to accumulate all the form url encoded key value pairs in the request.
            var formAccumulator = new KeyValueAccumulator();

            if (!(formSection is null))
            {
                MultipartSection section = formSection.Section;
                ContentDispositionHeaderValue contentDisposition = section.GetContentDispositionHeader();

                // Do not limit the key name length here because the
                // multipart headers length limit is already in effect.
                StringSegment key      = HeaderUtilities.RemoveQuotes(contentDisposition.Name);
                Encoding      encoding = GetEncoding(section);

                using (var streamReader = new StreamReader(
                           section.Body,
                           encoding,
                           detectEncodingFromByteOrderMarks: true,
                           bufferSize: 1024,
                           leaveOpen: true))
                {
                    // The value length limit is enforced by MultipartBodyLengthLimit
                    string value = await streamReader.ReadToEndAsync();

                    if (String.Equals(value, "undefined", StringComparison.OrdinalIgnoreCase))
                    {
                        value = String.Empty;
                    }

                    formAccumulator.Append(key.Value, value);
                    if (formAccumulator.ValueCount > FormReader.DefaultValueCountLimit)
                    {
                        throw new InvalidDataException($"Form key count limit {FormReader.DefaultValueCountLimit} exceeded.");
                    }
                }
            }

            // Bind form data to a model
            return(new FormValueProvider(
                       BindingSource.Form,
                       new FormCollection(formAccumulator.GetResults()),
                       CultureInfo.CurrentCulture));
        }
        private async Task <IFormCollection> InnerReadFormAsync(CancellationToken cancellationToken)
        {
            if (!HasFormContentType)
            {
                throw new InvalidOperationException("Incorrect Content-Type: " + _request.ContentType);
            }

            cancellationToken.ThrowIfCancellationRequested();

            //if (_options.BufferBody)
            //{
            //    _request.EnableRewind(_options.MemoryBufferThreshold, _options.BufferBodyLengthLimit);
            //}

            FormCollection     formFields = null;
            FormFileCollection files      = null;

            // Some of these code paths use StreamReader which does not support cancellation tokens.
            using (cancellationToken.Register((state) => ((HttpContext)state).Abort(), _request.HttpContext))
            {
                var contentType = ContentType;
                // Check the content-type
                if (HasApplicationFormContentType(contentType))
                {
                    var encoding = FilterEncoding(contentType.Encoding);
                    using (var formReader = new FormReader(_request.Body, encoding)
                    {
                        ValueCountLimit = _options.ValueCountLimit,
                        KeyLengthLimit = _options.KeyLengthLimit,
                        ValueLengthLimit = _options.ValueLengthLimit,
                    })
                    {
                        formFields = new FormCollection(await formReader.ReadFormAsync(cancellationToken));
                    }
                }
                else if (HasMultipartFormContentType(contentType))
                {
                    var formAccumulator = new KeyValueAccumulator();

                    var boundary        = GetBoundary(contentType, _options.MultipartBoundaryLengthLimit);
                    var multipartReader = new MultipartReader(boundary, _request.Body)
                    {
                        HeadersCountLimit  = _options.MultipartHeadersCountLimit,
                        HeadersLengthLimit = _options.MultipartHeadersLengthLimit,
                        BodyLengthLimit    = _options.MultipartBodyLengthLimit,
                    };
                    var section = await multipartReader.ReadNextSectionAsync(cancellationToken);

                    while (section != null)
                    {
                        // Parse the content disposition here and pass it further to avoid reparsings
                        ContentDispositionHeaderValue contentDisposition;
                        ContentDispositionHeaderValue.TryParse(section.ContentDisposition, out contentDisposition);

                        if (contentDisposition.IsFileDisposition())
                        {
                            var fileSection = new FileMultipartSection(section, contentDisposition);

                            FormFile file;
                            if (section.Body.CanSeek)
                            {
                                // Find the end
                                await section.Body.ConsumeAsync(cancellationToken);

                                var name     = fileSection.Name;
                                var fileName = fileSection.FileName;


                                if (section.BaseStreamOffset.HasValue)
                                {
                                    // Relative reference to buffered request body
                                    file = new StreamFormFile(_request.Body, section.BaseStreamOffset.Value, section.Body.Length, name, fileName);
                                }
                                else
                                {
                                    // Individually buffered file body
                                    file = new StreamFormFile(section.Body, 0, section.Body.Length, name, fileName);
                                }
                                file.Headers = new HeaderDictionary(section.Headers);
                            }
                            else
                            {
                                //// Enable buffering for the file if not already done for the full body
                                //section.EnableRewind(
                                //    _request.HttpContext.Response.RegisterForDispose,
                                //    _options.MemoryBufferThreshold, _options.MultipartBodyLengthLimit);

                                //read all bytes.
                                var bytes = await section.Body.ReadAllBytesAsync(cancellationToken);

                                var name     = fileSection.Name;
                                var fileName = fileSection.FileName;

                                if (section.BaseStreamOffset.HasValue)
                                {
                                    // Relative reference to buffered request body
                                    file = new BufferedFormFile(new ArraySegment <byte>(bytes, (int)section.BaseStreamOffset.Value, (int)section.Body.Length), name, fileName);
                                }
                                else
                                {
                                    // Individually buffered file body
                                    file = new BufferedFormFile(new ArraySegment <byte>(bytes, 0, (int)section.Body.Length), name, fileName);
                                }
                                file.Headers = new HeaderDictionary(section.Headers);
                            }

                            if (files == null)
                            {
                                files = new FormFileCollection();
                            }
                            if (files.Count >= _options.ValueCountLimit)
                            {
                                throw new InvalidDataException($"Form value count limit {_options.ValueCountLimit} exceeded.");
                            }
                            files.Add(file);
                        }
                        else if (contentDisposition.IsFormDisposition())
                        {
                            var formDataSection = new FormMultipartSection(section, contentDisposition);

                            // Content-Disposition: form-data; name="key"
                            //
                            // value

                            // Do not limit the key name length here because the mulipart headers length limit is already in effect.
                            var key   = formDataSection.Name;
                            var value = await formDataSection.GetValueAsync();

                            formAccumulator.Append(key, value);
                            if (formAccumulator.ValueCount > _options.ValueCountLimit)
                            {
                                throw new InvalidDataException($"Form value count limit {_options.ValueCountLimit} exceeded.");
                            }
                        }
                        else
                        {
                            System.Diagnostics.Debug.Assert(false, "Unrecognized content-disposition for this section: " + section.ContentDisposition);
                        }

                        section = await multipartReader.ReadNextSectionAsync(cancellationToken);
                    }

                    if (formAccumulator.HasValues)
                    {
                        formFields = new FormCollection(formAccumulator.GetResults(), files);
                    }
                }
            }

            // Rewind so later readers don't have to.
            if (_request.Body.CanSeek)
            {
                _request.Body.Seek(0, SeekOrigin.Begin);
            }

            if (formFields != null)
            {
                Form = formFields;
            }
            else if (files != null)
            {
                Form = new FormCollection(null, files);
            }
            else
            {
                Form = FormCollection.Empty;
            }

            return(Form);
        }
        public async Task <IActionResult> UploadFile([FromRoute] string username, [FromRoute] string directoryPath)
        {
            //string requestContentType = Request.ContentType;
            //bool hasFormContentType = Request.HasFormContentType;

            // Accumulate all form key-value pairs in the request (in case we want to do something with other form-fields that are not only files)
            KeyValueAccumulator         formAccumulator   = new KeyValueAccumulator();
            Dictionary <string, string> sectionDictionary = new Dictionary <string, string>();

            // get off the boundary appended by the form-post
            string boundary = GetBoundary(Request.ContentType);

            try // FIX THIS HUGE TRY BLOCK
            {
                var reader = new MultipartReader(boundary, Request.Body);
                MultipartSection section = null;

                section = await reader.ReadNextSectionAsync();

                while (section != null)
                {
                    // Get the conten header disposition for checking if we are handling a file-form-field or just any other type of field
                    ContentDispositionHeaderValue contentDisposition;
                    var hasContentDispositionHeader = ContentDispositionHeaderValue.TryParse(section.ContentDisposition, out contentDisposition);

                    if (hasContentDispositionHeader)
                    {
                        if (contentDisposition.IsFileDisposition())
                        {
                            // Fetch data from header (Delete this after we implement authentication)
                            var appId       = Request.Headers["AppId"];
                            var phonenumber = Request.Headers["OwnerPhoneNumber"];
                            var firstname   = Request.Headers["OwnerFirstName"];
                            var lastname    = Request.Headers["OwnerLastName"];

                            // Here we handle file fields
                            string fileName   = contentDisposition.FileName.ToString().Replace(" ", "");
                            string UploadsDir = Path.Combine(appEnv.WebRootPath, $"dataspace/{username}");

                            // If the BASE dir doesn't exist, create it
                            Directory.CreateDirectory(UploadsDir);

                            string physicalPath;
                            string url;
                            // Check for subdir
                            if (String.IsNullOrWhiteSpace(directoryPath))
                            {
                                physicalPath  = Path.Combine(UploadsDir, fileName); // Prepare file name and path for writing
                                url           = $"{Request.Scheme}://{Request.Host}/dataspace/{username}/files/{fileName}";
                                directoryPath = "";
                            }
                            else
                            {
                                physicalPath = Path.Combine(UploadsDir, directoryPath, fileName); // Prepare file name and path for writing
                                url          = $"{Request.Scheme}://{Request.Host}/dataspace/{username}/files/{directoryPath}/{fileName}";
                            }

                            //// We can also use a temp location for now eg. AppData on Windows
                            //var targetFilePath = Path.GetTempFileName();

                            // Createa a stream and write the request (section-field) body/data
                            using (var targetStream = System.IO.File.Create(physicalPath))
                            {
                                // Copy file to disk
                                await section.Body.CopyToAsync(targetStream);


                                // TODO: Generate metadata and save the path into a FileMicroservice with SignalR (db handler)
                                FileDto newFile = new FileDto
                                {
                                    Name         = fileName,
                                    Path         = directoryPath,
                                    Url          = url,
                                    MimeType     = section.ContentType,
                                    FileSizeInKB = (int)(targetStream.Length / 1024) // Test
                                };

                                dataSpaceSignalRClient.SaveFileMetadata(phonenumber, newFile); // rename stuff like this to nodePath
                            }
                        }
                        else if (contentDisposition.IsFormDisposition())
                        {
                            // Remove this (?)
                            // Here we handle other form fields
                            StringSegment fieldName = HeaderUtilities.RemoveQuotes(contentDisposition.Name);

                            FormMultipartSection formMultipartSection = section.AsFormDataSection();
                            string fieldValue = await formMultipartSection.GetValueAsync().ConfigureAwait(false);

                            sectionDictionary.Add(formMultipartSection.Name, fieldValue);

                            using (var streamReader = new StreamReader(section.Body))
                            {
                                if (String.Equals(fieldValue, "undefined", StringComparison.OrdinalIgnoreCase))
                                {
                                    fieldValue = String.Empty;
                                }
                                formAccumulator.Append(fieldName.ToString(), fieldValue);
                            }
                        }
                    }
                    // Read next section/field
                    section = await reader.ReadNextSectionAsync();
                }
            }
            catch (Exception e)
            {
                // handle any unread or errors on errors while streaming and writing received data
                return(BadRequest());
            }

            //// Remove this (?)
            //// Handle all the non-file fields either here, or above while reading the streams already eg. chosen parent directory
            //var result = sectionDictionary;
            //var frmResults = formAccumulator.GetResults();

            return(Ok());
        }
Exemple #6
0
        public async Task <ParseBodyAndSavePacketsResult> ParseBodyAndSavePackets(MessageHeaders messageHeaders, HttpRequest request)
        {
            var cancellationToken = request.HttpContext.RequestAborted;

            cancellationToken.ThrowIfCancellationRequested();

            var    format   = messageHeaders.Format ?? TransportConstants.RequestFormDataFormat;
            string boundary = null;

            if (format == TransportConstants.RequestFormDataFormat)
            {
                int fileSectionBufferSize = 81920;

                // если данные formdata упорядочены в потоке запроса (бинарные данные должны идти после большинства метаданных о пакете)
                if (messageHeaders.Hints?.Contains(MessageHints.OrderedFormData) == true)
                {
                    var indexToPacketDataItem    = new Dictionary <int, PacketFormDataItem>();
                    var indexToPacketBytes       = new Dictionary <int, byte[]>();
                    var indexToConfigurationItem = new Dictionary <int, ConfigurationRequestDataItem>();

                    if (MediaTypeHeaderValue.TryParse(request.ContentType, out var contentType))
                    {
                        boundary = HeaderUtilities.GetBoundary(contentType, 70);
                    }

                    var multipartReader = new MultipartReader(boundary, request.Body)
                    {
                        //ValueCountLimit = _options.ValueCountLimit,
                        //KeyLengthLimit = _options.KeyLengthLimit,
                        //ValueLengthLimit = _options.ValueLengthLimit,

                        HeadersCountLimit  = int.MaxValue,
                        HeadersLengthLimit = int.MaxValue,
                        BodyLengthLimit    = long.MaxValue,
                    };

                    //PacketFormDataItem current = null;
                    //byte[] currentBytes = null;
                    var agentId = messageHeaders.GetAgentIdData();
                    var section = await multipartReader.ReadNextSectionAsync(cancellationToken);

                    while (section != null)
                    {
                        // Parse the content disposition here and pass it further to avoid reparsings
                        if (!ContentDispositionHeaderValue.TryParse(section.ContentDisposition, out var contentDisposition))
                        {
                            throw new InvalidDataException("Form section has invalid Content-Disposition value: " + section.ContentDisposition);
                        }

                        if (contentDisposition.IsFileDisposition())
                        {
                            var fileSection = new FileMultipartSection(section, contentDisposition);

                            var name     = fileSection.Name;
                            var fileName = fileSection.FileName;
                            var packetId = fileSection.FileName;
                            var result   = GetFormPathDataFromEntry(name);
                            if (result != null && result.Parts.Count == 3 && result.Parts[0] == TransportConstants.FormDataPacketsProp)
                            {
                                if (int.TryParse(result.Parts[1], out var index))
                                {
                                    var item        = indexToPacketDataItem[index];
                                    var providerKey = item.ProviderKey;

                                    var packetItem = indexToPacketDataItem[index];
                                    if (packetItem.PacketId != packetId)
                                    {
                                        throw new InvalidDataException($"Incorrect format for form-data message. Section {name} has invalid FileName.");
                                    }

                                    var bytes = await ReadToEnd(fileSection.FileStream, fileSectionBufferSize, cancellationToken);

                                    indexToPacketBytes.Add(index, bytes);
                                }
                                else
                                {
                                    throw new InvalidDataException($"Incorrect format for form-data message. Section {name} does not have index suffix.");
                                }
                            }
                            else
                            {
                                throw new InvalidDataException($"Incorrect format for form-data message. Section {name} incorrect.");
                            }
                        }
                        else if (contentDisposition.IsFormDisposition())
                        {
                            var formDataSection = new FormMultipartSection(section, contentDisposition);

                            // Content-Disposition: form-data; name="key"
                            //
                            // value

                            // Do not limit the key name length here because the multipart headers length limit is already in effect.
                            var key   = formDataSection.Name;
                            var value = await formDataSection.GetValueAsync();

                            var result = GetFormPathDataFromEntry(key);
                            if (result != null && result.Parts.Count == 3 && result.Parts[0] == TransportConstants.FormDataPacketsProp)
                            {
                                if (int.TryParse(result.Parts[1], out var index))
                                {
                                    if (!indexToPacketDataItem.TryGetValue(index, out var dataItem))
                                    {
                                        //// сохраняем предыдущий
                                        //if (current != null)
                                        //{
                                        //    await this.packetsStore.AddIfNotExistsPacketPartAsync(agentId, this.CreateAddPacketRequest(current, currentBytes));
                                        //}

                                        dataItem = new PacketFormDataItem();
                                        indexToPacketDataItem.Add(index, dataItem);
                                    }

                                    if (!dataItem.FillProperty(result.Parts[2], value))
                                    {
                                        throw new InvalidDataException($"Incorrect format for form-data message. Section {key} incorrect.");
                                    }
                                }
                                else
                                {
                                    throw new InvalidDataException($"Incorrect format for form-data message. Section {key} does not have index suffix.");
                                }
                            }
                            else if (result != null && result.Parts.Count == 3 && result.Parts[0] == TransportConstants.FormDataConfigurationProp)
                            {
                                if (int.TryParse(result.Parts[1], out var index))
                                {
                                    if (!indexToConfigurationItem.TryGetValue(index, out var dataItem))
                                    {
                                        dataItem = new ConfigurationRequestDataItem();
                                        indexToConfigurationItem.Add(index, dataItem);
                                    }

                                    if (!dataItem.FillProperty(result.Parts[2], value))
                                    {
                                        throw new InvalidDataException($"Incorrect format for form-data message. Section {key} incorrect.");
                                    }
                                }
                                else
                                {
                                    throw new InvalidDataException($"Incorrect format for form-data message. Section {key} does not have index suffix.");
                                }
                            }
                            else
                            {
                                // ignore or throw?
                            }

                            //if (formAccumulator.ValueCount > _options.ValueCountLimit)
                            //{
                            //    throw new InvalidDataException($"Form value count limit {_options.ValueCountLimit} exceeded.");
                            //}
                        }
                        else
                        {
                            System.Diagnostics.Debug.Assert(false, "Unrecognized content-disposition for this section: " + section.ContentDisposition);
                        }

                        section = await multipartReader.ReadNextSectionAsync(cancellationToken);
                    }

                    // сохраняем все
                    var addResult = indexToPacketDataItem.Any()
                                ? await this.packetsStore.AddIfNotExistsPacketsPartsAsync(agentId, indexToPacketDataItem.Select(x =>
                    {
                        var bytes = indexToPacketBytes[x.Key];
                        return(this.CreateAddPacketRequest(agentId, x.Value, bytes));
                    }).ToList())
                                : AddAddPacketsPartsResult.EmptyResult();

                    return(new ParseBodyAndSavePacketsResult
                    {
                        TransferedPackets = indexToPacketDataItem.Values
                                            .Select(x => {
                            var fromAddResult = addResult.Results.Single(r => r.Request.PacketId == x.PacketId);
                            return new TransferedPacketResponse
                            {
                                PacketId = x.PacketId,
                                ProviderKey = x.ProviderKey,
                                //AgentIdData = agentId,
                                Result = fromAddResult.Success
                                                ? TransferedProcessingResult.Saved
                                                : TransferedProcessingResult.Error,
                                StorageToken = fromAddResult.StorageToken,
                                Id = fromAddResult.Id,
                            };
                        }).ToList(),
                        ConfigurationsStats = indexToConfigurationItem.Values,
                    });
                }
                else
                {
                    var indexToPacketDataItem    = new Dictionary <string, PacketFormDataItem>();
                    var indexToPacketBytes       = new Dictionary <string, byte[]>();
                    var indexToConfigurationItem = new Dictionary <string, ConfigurationRequestDataItem>();
                    var agentId = messageHeaders.GetAgentIdData();

                    foreach (var item in request.Form)
                    {
                        var key   = item.Key;
                        var value = item.Value;

                        var result = GetFormPathDataFromEntry(key);
                        if (result != null && result.Parts.Count == 3 && result.Parts[0] == TransportConstants.FormDataPacketsProp)
                        {
                            var index = result.Parts[1];
                            if (!indexToPacketDataItem.TryGetValue(index, out var dataItem))
                            {
                                dataItem = new PacketFormDataItem();
                                indexToPacketDataItem.Add(index, dataItem);
                            }

                            if (!dataItem.FillProperty(result.Parts[2], value))
                            {
                                throw new InvalidDataException($"Incorrect format for form-data message. Section {key} incorrect.");
                            }
                        }

                        if (result != null && result.Parts.Count == 3 && result.Parts[0] == TransportConstants.FormDataConfigurationProp)
                        {
                            var index = result.Parts[1];
                            if (!indexToConfigurationItem.TryGetValue(index, out var dataItem))
                            {
                                dataItem = new ConfigurationRequestDataItem();
                                indexToConfigurationItem.Add(index, dataItem);
                            }

                            if (!dataItem.FillProperty(result.Parts[2], value))
                            {
                                throw new InvalidDataException($"Incorrect format for form-data message. Section {key} incorrect.");
                            }
                        }
                    }

                    foreach (var file in request.Form.Files)
                    {
                        var pair = indexToPacketDataItem
                                   .FirstOrDefault(x => x.Value.PacketId == file.FileName);

                        if (default(KeyValuePair <string, PacketFormDataItem>).Equals(pair))
                        {
                            var item        = pair.Value;
                            var providerKey = item.ProviderKey;
                            var packetId    = item.PacketId;

                            using (var fileStream = file.OpenReadStream())
                            {
                                var currentBytes = await ReadToEnd(fileStream, fileSectionBufferSize, cancellationToken);

                                indexToPacketBytes.Add(pair.Key, currentBytes);
                            }
                        }
                    }

                    // сохраняем все
                    var addResult = indexToPacketDataItem.Any()
                                ? await this.packetsStore.AddIfNotExistsPacketsPartsAsync(agentId, indexToPacketDataItem.Select(x =>
                    {
                        var bytes = indexToPacketBytes[x.Key];
                        return(this.CreateAddPacketRequest(agentId, x.Value, bytes));
                    }).ToList())
                                : AddAddPacketsPartsResult.EmptyResult();

                    return(new ParseBodyAndSavePacketsResult
                    {
                        TransferedPackets = indexToPacketDataItem.Values
                                            .Select(x =>
                        {
                            var fromAddResult = addResult.Results
                                                .Single(r => r.Request.PacketId == x.PacketId);
                            return new TransferedPacketResponse
                            {
                                PacketId = x.PacketId,
                                ProviderKey = x.ProviderKey,
                                //AgentIdData = agentId,
                                Result = fromAddResult.Success
                                                                    ? TransferedProcessingResult.Saved
                                                                    : TransferedProcessingResult.Error,
                                StorageToken = fromAddResult.StorageToken,
                                Id = fromAddResult.Id,
                            };
                        }).ToList(),
                        ConfigurationsStats = indexToConfigurationItem.Values,
                    });
                }
            }
            else
            {
                throw new NotSupportedException($"format {format} is not supported");
            }
        }
        private async Task <FormCollection> GetMultipartFormCollection(ModificationContext modificationContext)
        {
            var formAccumulator = new KeyValueAccumulator();

            var multipartReader = new MultipartReader(_originalBoundary, modificationContext.ContentStream)
            {
                HeadersCountLimit  = _options.Value.FormOptions.MultipartHeadersCountLimit,
                HeadersLengthLimit = _options.Value.FormOptions.MultipartHeadersLengthLimit,
                BodyLengthLimit    = _options.Value.FormOptions.MultipartBodyLengthLimit,
            };

            var section = await multipartReader.ReadNextSectionAsync();

            while (section != null)
            {
                ContentDispositionHeaderValue contentDisposition;
                ContentDispositionHeaderValue.TryParse(section.ContentDisposition, out contentDisposition);

                if (contentDisposition.IsFileDisposition())
                {
                    var fileSection = new FileMultipartSection(section, contentDisposition);

                    await section.Body.DrainAsync(new System.Threading.CancellationToken());

                    FormFile file;

                    if (contentDisposition.FileName == "\"\"")
                    {
                        file = new FormFile(
                            section.Body,
                            0,
                            section.Body.Length,
                            fileSection.Name,
                            fileSection.FileName
                            );
                    }
                    else
                    {
                        file = new FormFile(
                            section.BaseStreamOffset.HasValue ? modificationContext.ContentStream : section.Body,
                            section.BaseStreamOffset.HasValue ? section.BaseStreamOffset.Value : 0,
                            section.Body.Length,
                            fileSection.Name,
                            fileSection.FileName
                            );
                    }

                    file.Headers = new HeaderDictionary(section.Headers);

                    if (_formFiles == null)
                    {
                        _formFiles = new FormFileCollection();
                    }

                    if (_formFiles.Count >= _options.Value.FormOptions.ValueCountLimit)
                    {
                        throw new InvalidDataException($"Form value count limit {_options.Value.FormOptions.ValueCountLimit} exceeded.");
                    }

                    _formFiles.Add(file);
                }
                else if (contentDisposition.IsFormDisposition())
                {
                    var formDataSection = new FormMultipartSection(section, contentDisposition);

                    var value = await formDataSection.GetValueAsync();

                    formAccumulator.Append(formDataSection.Name, value);

                    if (formAccumulator.ValueCount > _options.Value.FormOptions.ValueCountLimit)
                    {
                        throw new InvalidDataException($"Form value count limit {_options.Value.FormOptions.ValueCountLimit} exceeded.");
                    }
                }
                else
                {
                    throw new InvalidDataException($"Unrecognized content-disposition for this section: {section.ContentDisposition}");
                }

                section = await multipartReader.ReadNextSectionAsync();
            }

            if (formAccumulator.HasValues || _formFiles != null)
            {
                return(new FormCollection(formAccumulator.HasValues ? formAccumulator.GetResults() : null, _formFiles));
            }
            else
            {
                return(null);
            }
        }
Exemple #8
0
        public async Task <IActionResult> Upload()
        {
            string multipartBoundary = Request.GetMultipartBoundary();

            if (string.IsNullOrEmpty(multipartBoundary))
            {
                Response.StatusCode = 400;

                return(Json(new UploadResult
                {
                    Succeeded = false,
                    Description = $"Expected a multipart request, but got '{Request.ContentType}'."
                }));
            }

            var formAccumulator      = new KeyValueAccumulator();
            var reader               = new MultipartReader(multipartBoundary, HttpContext.Request.Body);
            MultipartSection section = await reader.ReadNextSectionAsync();

            string fileGuid      = Guid.NewGuid().ToString();
            string userId        = _userManager.GetUserId(HttpContext.User);
            string fileExtension = string.Empty;

            while (section != null)
            {
                FileMultipartSection fileSection = section.AsFileSection();

                if (fileSection != null)
                {
                    string fileName = fileSection.FileName;
                    fileExtension = Path.GetExtension(fileName);
                    string targetFolderPath = Path.Combine(_hostingEnvironment.WebRootPath, _appSettings.UploadsFolder, userId);
                    string targetFilePath   = Path.Combine(_hostingEnvironment.WebRootPath, _appSettings.UploadsFolder, userId, $"{fileGuid}{fileExtension}");

                    if (!Directory.Exists(targetFolderPath))
                    {
                        Directory.CreateDirectory(targetFolderPath);
                    }

                    using (var targetStream = System.IO.File.Create(targetFilePath))
                    {
                        await fileSection.FileStream.CopyToAsync(targetStream);

                        _logger.LogInformation($"Copied the uploaded file '{fileName}' to '{targetFilePath}'.");
                    }
                }
                else
                {
                    FormMultipartSection formSection = section.AsFormDataSection();

                    if (formSection != null)
                    {
                        string name  = formSection.Name;
                        string value = await formSection.GetValueAsync();

                        formAccumulator.Append(name, value);

                        if (formAccumulator.ValueCount > FormReader.DefaultValueCountLimit)
                        {
                            throw new InvalidDataException($"Form key count limit {FormReader.DefaultValueCountLimit} exceeded.");
                        }
                    }
                }

                section = await reader.ReadNextSectionAsync();
            }

            var uploadResult = new UploadResult
            {
                Succeeded   = true,
                Description = "File was uploaded successfully",
                FileUrl     = $"{_appSettings.Domain}/{_appSettings.UploadsFolder}/{userId}/{fileGuid}{fileExtension}",
                FileName    = $"{fileGuid}{fileExtension}"
            };

            return(Json(uploadResult));
        }