从响应消息 C# .NET 4.8 解析多部分文件

Posted

技术标签:

【中文标题】从响应消息 C# .NET 4.8 解析多部分文件【英文标题】:Parsing Multipart files from Response Message C# .NET 4.8 【发布时间】:2021-12-15 09:26:04 【问题描述】:

我的HttpClient 收到一个多部分响应(在HttpResponseMessage 对象中),我需要从响应中解析/读出文件。

要求是用.NET标准库(不高于.NET 4.8)解决。

以前我使用现在备受推崇的System.Net.Http.Formating 程序集或Microsoft.AspNet.WebApi.Client 程序集来执行此操作。像这样:

            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);
             

是否有任何其他 .NET 标准库(不高于 .NET 4.8)可以像上面的代码一样读取文件? 我只找到了MultipartContent,但是这个类只能创建一个MultipartForm 发送到Webserver。

非常感谢任何帮助。

【问题讨论】:

【参考方案1】:

是的,您可以使用MimeKitLite 来执行此操作。

使用 MimeKitLite v3.0 完成此任务的最简单方法如下所示:

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;
    

【讨论】:

以上是关于从响应消息 C# .NET 4.8 解析多部分文件的主要内容,如果未能解决你的问题,请参考以下文章

在 Swift 中解析多部分 SOAP 响应

解析多部分消息,仅正文

c# HTTP Multipart MIME 解析器

来自 C# 客户端的多部分表单

C# 10 完整特性介绍

.Net Framework 4.8 for asp.net 中是不是包含 C# 7.3 编译器? [复制]