Webapi Formdata Upload (To Db) with Extra Parameters

Webapi formdata upload (to DB) with extra parameters

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<bool> _isFormData = new Collection<bool>();
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<string> 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:

<form action="/api/values" method="post" enctype="multipart/form-data">
<input name="myParameter" type="hidden" value="i dont do anything interesting"/>
<input type="file" name="file1" />
<input type="file" name="file2" />
<input type="submit" value="OK" />
</form>

Webapi ajax formdata upload with extra parameters

Posting the FormData object results in a request with content type multipart/form-data. You have to read the request content like so:

[HttpPost]
public async Task<string> Upload()
{
var provider = new MultipartFormDataStreamProvider("C:\\Somefolder");
await Request.Content.ReadAsMultipartAsync(provider);

var myParameter = provider.FormData.GetValues("myParameter").FirstOrDefault();
var count = provider.FileData.Count;

return count + " / " + myParameter;
}

BTW, this will save the file in the path specified, which is C:\\SomeFolder and you can get the local file name using provider.FileData[0].LocalFileName;

Please take a look at MSDN code sample and Henrik's blog entry.

Post file as well as some parameter to web api

I was able to post successfully with the following console application (based on this post):

    static void Main(string[] args)
{
RunAsync().Wait();
}

static async Task RunAsync()
{
using (var client = new HttpClient())
{
client.BaseAddress = new Uri("http://localhost:3963/");
client.DefaultRequestHeaders.Accept.Clear();
client.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json"));

string filepath = "C:/Users/Popper/Desktop/Stackoverflow/MatchPositions.PNG";
string filename = "MatchPositions.PNG";

MultipartFormDataContent content = new MultipartFormDataContent();
ByteArrayContent fileContent = new ByteArrayContent(System.IO.File.ReadAllBytes(filepath));
fileContent.Headers.ContentDisposition = new ContentDispositionHeaderValue("attachment") { FileName = filename };
content.Add(fileContent);

HttpResponseMessage response = await client.PostAsync("api/Upload?participantsId=2&taskId=77&EnteredAnswerOptionId=235", content);
string returnString = await response.Content.ReadAsAsync<string>();
}
}

Angular Upload File and Additional Data ASP WebAPI

If you want to include a form with files as parameter to an action it is necessary to add a custom media formatter. Fortunately someone already created a Nuget-package for this. Configuration is easy. Install the package and add a line to your WebApiConfig-file.

This package allows you to use a HttpFile-object which captures your file either directly as a parameter or inside a model. From the docs:

[HttpPost]
public void PostFileBindRawFormData(MultipartDataMediaFormatter.Infrastructure.FormData formData)
{
HttpFile file;
formData.TryGetValue(<key>, out file);
}

or

public class PersonModel
{
public string FirstName {get; set;}
public string LastName {get; set;}
public DateTime? BirthDate {get; set;}
public HttpFile AvatarImage {get; set;}
public List<HttpFile> Attachments {get; set;}
public List<PersonModel> ConnectedPersons {get; set;}
}

//api controller example
[HttpPost]
public void PostPerson(PersonModel model)
{
//do something with the model
}

asp.net core web API file upload and form-data multiple parameter passing to method

[HttpPost("[action]")]
[Consumes("multipart/form-data")]
public IActionResult UploadImage([FromForm] FileInputModel Files)
{

return Ok();
}

public class FileInputModel
{
public IFormFile File { get; set; }
public string Param { get; set; }
}

Need to add [FromForm] before the parameter model after I add [FromForm] code works perfectly.

File Upload with Additional Parameters as JSON

The Content-Type should be multipart/form-data not application/json for your code to work. A typical content type looks like this:

Content-Type: multipart/form-data; boundary=----WebKitFormBoundaryvmxBBVAoH3KRsa9L

The actual post data then contains blocks of 'data' separated by the boundary. These blocks can contain a content type, but they don't have to. If you're doing file uploads the content type should not matter - the filename is what determines the type.

All that said, with WebAPI you shouldn't be using the ASP.NET HttpContext object, but rather the lower level Web API semantics. While what you're doing works as long as you run inside of the ASP.NET stack, if you self-host or maybe in the future run ontop of a different stack like OWin/Katana this code will no longer work.

To handle file uploads with Web API specific code check out this blog post from Filip W.

http://www.strathweb.com/2012/04/html5-drag-and-drop-asynchronous-multi-file-upload-with-asp-net-webapi/



Related Topics



Leave a reply



Submit