5

I have an ASP.Net Core Web API, with a controller POST method defined like this:

[HttpPost("SubmitFile")]
public async Task<IActionResult> SubmitFile(IFormFile file)
{
}

I have a client-side method to call the API SubmitFile() method, defined like this:

[HttpPost]
public async Task<IActionResult> Index(ICollection<IFormFile> files)
{
     using (var client = new HttpClient())
     {
         client.BaseAddress = new Uri(_options.SiteSpecificUrl);

         foreach (var file in files)
         {
             if (file.Length <= 0)
                 continue;

             var fileName = ContentDispositionHeaderValue.Parse(file.ContentDisposition).FileName.Trim('"');
             var fileContent = new StreamContent(file.OpenReadStream());
             fileContent.Headers.Add("X-FileName", fileName);
             fileContent.Headers.Add("X-ContentType", file.ContentType);

             var response = await client.PostAsync(_options.WebApiPortionOfUrl, fileContent);
         }
     }

    return View();
}

When the client send is performed, on the server side a break-point in SubmitFile() shows that the file argument is null. How can I correctly send the file? It is important to preserve the server-side API, as I have Swashbuckle/Swagger correctly generating a UI that can send the file.

3
  • 4
    I'm pretty sure you cannot accept an interface as a model bound parameter. You must use a concrete class. Commented Jun 16, 2017 at 16:26
  • You're referring to SubmitFile(IFormFile file)? Please explain... Commented Jun 16, 2017 at 17:29
  • No, like List<IFormFile>, not ICollection. Although, reading the documentation, it can accept an IEnumerable<IFormFile>. See learn.microsoft.com/en-us/aspnet/core/mvc/models/file-uploads Commented Jun 16, 2017 at 18:39

2 Answers 2

11

I've found a couple ways of doing this. Here is the simplest. Note that this is an ASP.Net Core client-side solution:

[HttpPost]
public async Task<IActionResult> Index(ICollection<IFormFile> files)
{
    using (var client = new HttpClient())
    {
        client.BaseAddress = new Uri(_options.SiteSpecificUrl);

        foreach (var file in files)
        {
            if (file.Length <= 0)
                continue;

            var fileName = ContentDispositionHeaderValue.Parse(file.ContentDisposition).FileName.Trim('"');

            using (var content = new MultipartFormDataContent())
            {
                content.Add(new StreamContent(file.OpenReadStream())
                {
                    Headers =
                    {
                        ContentLength = file.Length,
                        ContentType = new MediaTypeHeaderValue(file.ContentType)
                    }
                }, "File", fileName);

                var response = await client.PostAsync(_options.WebApiPortionOfUrl, content);
            }
        }
    }
}

This controller method is called from a .cshtml page as follows:

@{
    ViewData["Title"] = "Home Page";
}

<form method="post" asp-action="Index" asp-controller="Home" enctype="multipart/form-data">
    <input type="file" name="files" multiple />
    <input type="submit" value="Upload" />
</form>

This form displays two buttons, "Choose Files", which presents a "select files" dialog, and "Upload", which calls the HomeController.Index method.

Sign up to request clarification or add additional context in comments.

Comments

0

This worked for me:

Front-end:

HTML/CSS:

<div id="" class="col-xs-12 info-box">
<div class="col-xs-12">  
    <a role="button" data-toggle="collapse" href="#upload-sample" aria-expanded="false"> 
        <h3><span class="glyphicon glyphicon-upload"></span> Upload de Arquivo</h3>
    </a>
</div>   
<div id="upload-sample" class="col-xs-12 collapse">  
    <form method="post" enctype="multipart/form-data">
        <div>
            <div class="form-group attach" style="width: 100%;">
                <label>Select Excel File <button type="button" id="btnDownloadTemplate">(Download Template)</button></label>
                <div class="col-md-12"><input type="file" id="fUpload" name="files" multiple class="form-control" style="max-width: 400px;" /></div>
            </div>                
            <div class="filter-button" style="width: 100%;">                     
                <button onclick="AJAXSubmit(this); return false;" id="btnUpload" class="btn btn-primary">Send File and update data</button>
            </div>
        </div>
    </form>
</div>      

JavaScript:

async function AJAXSubmit(oFormElement) {
    const files = $('#fUpload').prop("files");
    const fdata = new FormData();
    for (var i = 0; i < files.length; i++) {
        fdata.append("files", files[i]);
    }
    if (files.length > 0) {
        Block();
        $.ajax({
            type: "POST",
            url: "/{{controllerName}}/OnPostUpload?handler=Upload",
            beforeSend: function (xhr) {
                xhr.setRequestHeader("XSRF-TOKEN",
                    $('input:hidden[name="__RequestVerificationToken"]').val());
            },
            data: fdata,
            contentType: false,
            processData: false,
            success: function (response) {
                document.getElementById("fUpload").value = "";
                //Unblock();                    
                //toastr.success(response, "File processed successfully");
                FilterHandlebarsF.Search(this, true);
            },
            error: function (response) {                    
                document.getElementById("fUpload").value = "";
                //Unblock();
                //toastr.warning(response, "Error on processing");
            }
        });
    }
    else {
        //toastr.warning("Please, select a file.");
    }
}

Back-end:

Startup.cs: services.AddAntiforgery(o => o.HeaderName = "XSRF-TOKEN");

Controller:

public ActionResult OnPostUpload(List<IFormFile> files)
    {
        try
        {
            var file = files.FirstOrDefault();
            var inputstream = file.OpenReadStream();

            XSSFWorkbook workbook = new XSSFWorkbook(stream);

            var FIRST_ROW_NUMBER = {{firstRowWithValue}};

            ISheet sheet = workbook.GetSheetAt(0);
            // Example: var firstCellRow = (int)sheet.GetRow(0).GetCell(0).NumericCellValue;

            for (int rowIdx = 2; rowIdx <= sheet.LastRowNum; rowIdx++)
               {
                  IRow currentRow = sheet.GetRow(rowIdx);

                  if (currentRow == null || currentRow.Cells == null || currentRow.Cells.Count() < FIRST_ROW_NUMBER) break;

                  var df = new DataFormatter();                

                  for (int cellNumber = {{firstCellWithValue}}; cellNumber < {{lastCellWithValue}}; cellNumber++)
                      {
                         //business logic & saving data to DB                        
                      }               
                 }
        }
        catch(Exception ex)
        {
            throw new FileFormatException($"Error on file processing - {ex.Message}");
        }
    }

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.