Webapi formdata upload (to DB) with extra parameters

后端 未结 4 1732
谎友^
谎友^ 2020-11-28 04:47

I need to upload file sending extra paramaters.

I have found the following post in stackoverflow: Webapi ajax formdata upload with extra parameters

It descri

4条回答
  •  情书的邮戳
    2020-11-28 05:09

    You can achieve this in a not-so-very-clean manner by implementing a custom DataStreamProvider that duplicates the logic for parsing FormData from multi-part content from MultipartFormDataStreamProvider.

    I'm not quite sure why the decision was made to subclass MultipartFormDataStreamProvider from MultiPartFileStreamProvider without at least extracting the code that identifies and exposes the FormData collection since it is useful for many tasks involving multi-part data outside of simply saving a file to disk.

    Anyway, the following provider should help solve your issue. You will still need to ensure that when you iterate the provider content you are ignoring anything that does not have a filename (specifically the statement streamProvider.Contents.Select() else you risk trying to upload the formdata to the DB). Hence the code that asks the provider is a HttpContent IsStream(), this is a bit of a hack but was the simplest was I could think to do it.

    Note that it is basically a cut and paste hatchet job from the source of MultipartFormDataStreamProvider - it has not been rigorously tested (inspired by this answer).

    public class MultipartFormDataMemoryStreamProvider : MultipartMemoryStreamProvider
    {
        private readonly Collection _isFormData = new Collection();
        private readonly NameValueCollection _formData = new NameValueCollection(StringComparer.OrdinalIgnoreCase);
    
        public NameValueCollection FormData
        {
            get { return _formData; }
        }
    
        public override Stream GetStream(HttpContent parent, HttpContentHeaders headers)
        {
            if (parent == null) throw new ArgumentNullException("parent");
            if (headers == null) throw new ArgumentNullException("headers");
    
            var contentDisposition = headers.ContentDisposition;
    
            if (contentDisposition != null)
            {
                _isFormData.Add(String.IsNullOrEmpty(contentDisposition.FileName));
                return base.GetStream(parent, headers);
            }
    
            throw new InvalidOperationException("Did not find required 'Content-Disposition' header field in MIME multipart body part.");
        }
    
        public override async Task ExecutePostProcessingAsync()
        {
            for (var index = 0; index < Contents.Count; index++)
            {
                if (IsStream(index))
                    continue;
    
                var formContent = Contents[index];
                var contentDisposition = formContent.Headers.ContentDisposition;
                var formFieldName = UnquoteToken(contentDisposition.Name) ?? string.Empty;
                var formFieldValue = await formContent.ReadAsStringAsync();
                FormData.Add(formFieldName, formFieldValue);
            }
        }
    
        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;
        }
    
        public bool IsStream(int idx)
        {
            return !_isFormData[idx];
        }
    }
    

    It can be used as follows (using TPL syntax to match your question):

    [HttpPost]
    public Task Post()
    {
        if (!Request.Content.IsMimeMultipartContent())
            throw new HttpResponseException(Request.CreateResponse(HttpStatusCode.NotAcceptable, "Invalid Request!"));
    
        var provider = new MultipartFormDataMemoryStreamProvider();
    
        return Request.Content.ReadAsMultipartAsync(provider).ContinueWith(p =>
        {
            var result = p.Result;
            var myParameter = result.FormData.GetValues("myParameter").FirstOrDefault();
    
            foreach (var stream in result.Contents.Where((content, idx) => result.IsStream(idx)))
            {
                var file = new FileData(stream.Headers.ContentDisposition.FileName);
                var contentTest = stream.ReadAsByteArrayAsync();
                // ... and so on, as per your original code.
    
            }
            return myParameter;
        });
    }
    

    I tested it with the following HTML form:

提交回复
热议问题