I need to upload a file using .net core.
I am using a multipart form and AJAX.
However I have a unique requirement. I need to be able to add data to the serialized array and then bind it to the controller through an ajax POST request and Model Binding.
I need to add an id, which i pass to the controller. Based on the id I decide to which row in a table I save the uploaded file details.
My Controller to which I am Posting:
[HttpPost]
public async Task<IActionResult> File(List<IFormFile> files)
{
long size = files.Sum(f => f.Length);
var filePath = Path.GetTempFileName();
foreach (var formFile in files)
{
if (formFile.Length > 0)
{
using (var stream = new FileStream(filePath, FileMode.Create))
{
await formFile.CopyToAsync(stream);
}
}
}
string fpath = filePath;
var query = from d in db.TableA
where d.File == null && d.id == id // This is where I need to compare the ID
select d;
foreach (TableA details in query)
{
details.File = filePath;
}
try
{
await db.SaveChangesAsync(); // await needed to hold execution
}
catch (Exception e)
{
Console.WriteLine(e);
}
return RedirectToAction("View");
}
where d.File == null && d.id == id // This is where I need to compare the ID
Multi Part Form:
<form method="post" id="typea" enctype="multipart/form-data" asp-controller="Main" asp-action="File">
<label class="btn btn-info"> Upload your document <input type="file" name="files" onchange="this.form.submit()" hidden />
</label>
</form>
My Ajax call:
$(document).ready(function () {
$('#typea').click(function () {
event.preventDefault();
var $form = $(this);
var serializedData = $form.serializeArray();
serializedData.push({ name: "ID", value: "typea" });
$.ajax({
url: "Main/File",
type: "POST",
data: serializedData
});
});
My problem is this:
If i push the array I cannot count on IFormFile Interface for model binding.
Can I somehow extend the IFormFIle interface?
Or is there a way I can do it without using the IFormFile. I tried to write my own model referencing from the IFormFIle interface but could not.
public interface IFormFile
{
string ContentType { get; }
string ContentDisposition { get; }
IHeaderDictionary Headers { get; }
long Length { get; }
string Name { get; }
string FileName { get; }
Stream OpenReadStream();
void CopyTo(Stream target);
Task CopyToAsync(Stream target, CancellationToken cancellationToken = null);
}
I could not use the methods of the interface, which is obvious. Pointers please :).
You don't need to implements (extends) the IFormFile
interface, a wrapper by composition is preferred over inheritance. Simply create a dummy POCO to hold the information :
public class IFormFilesWrapper{
public string Id {get;set;} // you might change the type to Guid / int /e.t.c
public IList<IFormFile> Files {get;set;}
}
and the action method will be :
[HttpPost]
public async Task<IActionResult> File(IFormFilesWrapper filesWrapper)
{
var id = filesWrapper.Id; // here's the Id posted by client
long size = filesWrapper.Files.Sum(f => f.Length);
var filePath = Path.GetTempFileName();
foreach (var formFile in filesWrapper.Files)
{
if (formFile.Length > 0)
{
using (var stream = new FileStream(filePath, FileMode.Create))
{
await formFile.CopyToAsync(stream);
}
}
}
// ...
}
As a side note, if I remember correctly, the $form.serializeArray()
doesn't work well for multipart/form-data
. So I use a plain FormData
to generate the payload :
$(document).ready(function () {
$('#typea>button').click(function () {
event.preventDefault();
var form = document.querySelector("#typea");
var formData = new FormData(form);
formData.append("ID","typea");
var xhr = new XMLHttpRequest();
xhr.open("POST","Main/File");
xhr.send(formData);
});
});
A screenshot :
Collected from the Internet
Please contact [email protected] to delete if infringement.
Comments