How does one correctly implement a MediaTypeFormatter to handle requests of type 'multipart/mixed'?

前端 未结 2 695
感情败类
感情败类 2020-12-24 02:44

Consider a web service written in ASP.NET Web API to accept any number files as a \'multipart/mixed\' request. The helper method mat look as follows (assuming _client

相关标签:
2条回答
  • 2020-12-24 03:03

    Have a look at this forum: http://forums.asp.net/t/1777847.aspx/1?MVC4+Beta+Web+API+and+multipart+form+data

    Here is a snippet of code (posted by imran_ku07) that might help you implement a custom formatter to handle the multipart/form-data:

    public class MultiFormDataMediaTypeFormatter : FormUrlEncodedMediaTypeFormatter
    {
        public MultiFormDataMediaTypeFormatter() : base()
        {
            this.SupportedMediaTypes.Add(new MediaTypeHeaderValue("multipart/form-data"));
        }
    
        protected override bool CanReadType(Type type)
        {
            return true;
        }
    
        protected override bool CanWriteType(Type type)
        {
            return false;
        }
    
        protected override Task<object> OnReadFromStreamAsync(Type type, Stream stream, HttpContentHeaders contentHeaders, FormatterContext formatterContext)
        {
            var contents = formatterContext.Request.Content.ReadAsMultipartAsync().Result;
            return Task.Factory.StartNew<object>(() =>
            {
                return new MultiFormKeyValueModel(contents);
            });
        }
    
        class MultiFormKeyValueModel : IKeyValueModel
        {
            IEnumerable<HttpContent> _contents;
            public MultiFormKeyValueModel(IEnumerable<HttpContent> contents)
            {
                _contents = contents;
            }
    
    
            public IEnumerable<string> Keys
            {
                get
                {
                    return _contents.Cast<string>();
                }
            }
    
            public bool TryGetValue(string key, out object value)
            {
                value = _contents.FirstDispositionNameOrDefault(key).ReadAsStringAsync().Result;
                return true;
            }
        }
    }
    

    You then need to add this formatter to your application. If doing self-host you can simply add it by including:

    config.Formatters.Insert(0, new MultiFormDataMediaTypeFormatter());
    

    before instantiating the HttpSelfHostServer class.

    -- EDIT --

    To parse binary streams you'll need another formatter. Here is one that I am using to parse images in one of my work projects.

    class JpegFormatter : MediaTypeFormatter
    {
        protected override bool CanReadType(Type type)
        {
            return (type == typeof(Binary));
        }
    
        protected override bool CanWriteType(Type type)
        {
            return false;
        }
    
        public JpegFormatter()
        {
            SupportedMediaTypes.Add(new MediaTypeHeaderValue("image/jpeg"));
            SupportedMediaTypes.Add(new MediaTypeHeaderValue("image/jpg"));
            SupportedMediaTypes.Add(new MediaTypeHeaderValue("image/png"));
        }
    
        protected override Task<object> OnReadFromStreamAsync(Type type, Stream stream, HttpContentHeaders contentHeaders, FormatterContext formatterContext)
        {
            return Task.Factory.StartNew(() =>
                {
                    byte[] fileBytes = new byte[stream.Length];
                    stream.Read(fileBytes, 0, (int)fileBytes.Length);
    
                   return (object)new Binary(fileBytes);
                }); 
        }
    
        protected override Task OnWriteToStreamAsync(Type type, object value, Stream stream, HttpContentHeaders contentHeaders, FormatterContext formatterContext, TransportContext transportContext)
        {
            throw new NotImplementedException();
        }
    }
    

    In your controller/action you'll want to do something along the lines of:

    public HttpResponseMessage UploadImage(Binary File) {
     //do something with your file
    }
    
    0 讨论(0)
  • 2020-12-24 03:11

    Take a look at this post https://stackoverflow.com/a/17073113/1944993 the answer of Kiran Challa is really nice!

    The essential part :

    Custom In-memory MultiaprtFormDataStreamProvider:

        public class InMemoryMultipartFormDataStreamProvider : MultipartStreamProvider
    {
        private NameValueCollection _formData = new NameValueCollection();
        private List<HttpContent> _fileContents = new List<HttpContent>();
    
        // Set of indexes of which HttpContents we designate as form data
        private Collection<bool> _isFormData = new Collection<bool>();
    
        /// <summary>
        /// Gets a <see cref="NameValueCollection"/> of form data passed as part of the multipart form data.
        /// </summary>
        public NameValueCollection FormData
        {
            get { return _formData; }
        }
    
        /// <summary>
        /// Gets list of <see cref="HttpContent"/>s which contain uploaded files as in-memory representation.
        /// </summary>
        public List<HttpContent> Files
        {
            get { return _fileContents; }
        }
    
        public override Stream GetStream(HttpContent parent, HttpContentHeaders headers)
        {
            // For form data, Content-Disposition header is a requirement
            ContentDispositionHeaderValue contentDisposition = headers.ContentDisposition;
            if (contentDisposition != null)
            {
                // We will post process this as form data
                _isFormData.Add(String.IsNullOrEmpty(contentDisposition.FileName));
    
                return new MemoryStream();
            }
    
            // If no Content-Disposition header was present.
            throw new InvalidOperationException(string.Format("Did not find required '{0}' header field in MIME multipart body part..", "Content-Disposition"));
        }
    
        /// <summary>
        /// Read the non-file contents as form data.
        /// </summary>
        /// <returns></returns>
        public override async Task ExecutePostProcessingAsync()
        {
            // Find instances of non-file HttpContents and read them asynchronously
            // to get the string content and then add that as form data
            for (int index = 0; index < Contents.Count; index++)
            {
                if (_isFormData[index])
                {
                    HttpContent formContent = Contents[index];
                    // Extract name from Content-Disposition header. We know from earlier that the header is present.
                    ContentDispositionHeaderValue contentDisposition = formContent.Headers.ContentDisposition;
                    string formFieldName = UnquoteToken(contentDisposition.Name) ?? String.Empty;
    
                    // Read the contents as string data and add to form data
                    string formFieldValue = await formContent.ReadAsStringAsync();
                    FormData.Add(formFieldName, formFieldValue);
                }
                else
                {
                    _fileContents.Add(Contents[index]);
                }
            }
        }
    
        /// <summary>
        /// Remove bounding quotes on a token if present
        /// </summary>
        /// <param name="token">Token to unquote.</param>
        /// <returns>Unquoted token.</returns>
        private static string UnquoteToken(string token)
        {
            if (String.IsNullOrWhiteSpace(token))
            {
                return token;
            }
    
            if (token.StartsWith("\"", StringComparison.Ordinal) && token.EndsWith("\"", StringComparison.Ordinal) && token.Length > 1)
            {
                return token.Substring(1, token.Length - 2);
            }
    
            return token;
        }}
    

    You can then the "MemoryMultiPartDataStreamProvider" in you webapi like this :

    var provider = await Request.Content.ReadAsMultipartAsync<InMemoryMultipartFormDataStreamProvider>(new InMemoryMultipartFormDataStreamProvider());
    
        //access form data
        NameValueCollection formData = provider.FormData;
    
        //access files
        IList<HttpContent> files = provider.Files;
    
    0 讨论(0)
提交回复
热议问题