Parsing Multipart files from Response Message C# .NET 4.8

Viewed 136

My HttpClient receives an Multipart Response (in an HttpResponseMessage object) and i need to parse/readout the Files from the Response.

The requirements are to solve it with an .NET standard library (not higher than .NET 4.8).

Previously i did this with the now pepreciated System.Net.Http.Formating assembly or the Microsoft.AspNet.WebApi.Client assembly. Like this:

            List<byte[]> lbaFiles = new List<byte[]>();

            StreamContent strCont = new StreamContent(response.Content.ReadAsStreamAsync());
            strCont.Headers.Add("Content-Type", response.Content.Headers.ContentType);

            MultipartMemoryStreamProvider multipart = await strCont.ReadAsMultipartAsync();

            foreach (HttpContent cont in multipart.Contents)
            {
                byte[] baFile = await cont.ReadAsByteArrayAsync();
                lbaFiles.Add(baFile);
            } 

Is there any other .NET standard library (not higher than .NET 4.8) that can read out the Files like the code above? I only found MultipartContent, but this class can only create an MultipartForm to send to an Webserver.

Any help is highly appreciated.

1 Answers

Yes, you can use MimeKitLite to do this.

The easiest way to accomplish this with MimeKitLite v3.0 will look something like this:

using (var contentStream = await response.Content.ReadAsStreamAsync ()) {
    var header = string.Format ("Content-Type:{0}\r\n\r\n", response.Content.Headers.ContentType);
    using (var headerStream = new MemoryStream (Encoding.UTF8.GetBytes (header), false)) {
        using (var chainedStream = new ChainedStream ()) {
            chained.Add (headerStream);
            chained.Add (contentStream);

            var reader = new MyMimeReader (chainedStream);

            await reader.ReadEntityAsync ();
        }
    }
}

MyMimeFilter.cs:

public class MyMimeReader : MimeReader
{
    static readonly byte[] EmptyInput = new byte[0];
    IMimeFilter _decoder;
    Stream _fileStream;
    string _fileName;

    public MyMimeReader (Stream stream) : base (stream, MimeFormat.Entity)
    {
    }

    protected override void OnHeadersBegin (long beginOffset, int beginLineNumber, CancellationToken cancellationToken)
    {
        // This marks the start of a new MIME entity, so reset our state.
        _decoder = DecoderFilter.Create (ContentEncoding.Default);
        _fileStream = null;
        _fileName = null;
    }

    protected override void OnHeaderRead (Header header, int beginLineNumber, CancellationToken cancellationToken)
    {
        switch (header.Id) {
        case HeaderId.ContentDisposition:
            // If the header is the Content-Disposition header, parse the
            // value to get the filename parameter value.
            if (ContentDisposition.TryParse (header.RawValue, out var disposition)) {
                if (!string.IsNullOrEmpty (disposition.FileName)) {
                    // Make sure to only use the file name and not a full path.
                    _fileName = Path.GetFileName (disposition.FileName);
                }
            }
            break;
        case HeaderId.ContentTransferEncoding:
            // If the header is the Content-Transfer-Encoding header,
            // parse the value so that we can decode the content.
            _decoder = DecoderFilter.Create (header.Value);
            break;
        }
    }

    protected override void OnMimePartBegin (ContentType contentType, long beginOffset, int beginLineNumber, CancellationToken cancellationToken)
    {
        // If there was a Content-Disposition header, then we'll use that filename.
        // Otherwise, check to see if there is a name parameter on the 
        // Content-Type header and use that. If all else fails, generate a
        // new filename.
        if (string.IsNullOrEmpty (_fileName)) {
            if (string.IsNullOrEmpty (contentType.Name)) {
                // Generate a new filename.
                _fileName = GenerateRandomFileName ();
            } else {
                _fileName = Path.GetFileName (contentType.Name);
            }
        }
    }

    protected override void OnMimePartContentBegin (long beginOffset, int beginLineNumber, CancellationToken cancellationToken)
    {
        // Create the file stream that we'll save the content to.
        _fileStream = File.Create (_fileName);
    }

    protected override Task OnMimePartContentReadAsync (byte[] buffer, int startIndex, int count, CancellationToken cancellationToken)
    {
        int outputIndex, outputLength;
        byte[] decoded;

        decoded = _decoder.Filter (buffer, startIndex, count, out outputIndex, out outputLength);

        await _currentFileStream.WriteAsync (decoded, outputIndex, outputLength, cancellationToken);
    }

    protected override Task OnMimePartContentEndAsync (long beginOffset, int beginLineNumber, long endOffset, int lines, NewLineFormat? newLineFormat, CancellationToken cancellationToken)
    {
        int outputIndex, outputLength;
        byte[] decoded;

        decoded = _decoder.Flush (EmptyInput, 0, 0, out outputIndex, out outputLength);

        if (outputLength > 0)
            await _fileStream.WriteAsync (decoded, outputIndex, outputLength, cancellationToken);

        await _fileStream.FlushAsync (cancellationToken);
        _fileStream.Dispose ();
        _fileStream = null;
    }
}
Related