0

I have asp.net mvc and asp.net core 3.1 amazon s3 projects. I am loading excel file in asp.net mvc and this excel file is saved in amazon s3. I performed the registration to amazon s3 with asp.net core web api. I use ajaxForm for file upload. What I want to try is to direct the file upload process to asp.net core web api via mvc controller. Another issue I can not do is to save the data contained in the file to the sql database after the excel file is saved in amazon s3. I tried to find results from different sources, but I was not successful. How can I do that?

This is my MVC View code:

@using (Html.BeginForm("Index", "Admin", FormMethod.Post, new { enctype = "multipart/form-data", id = "Myform" }))
                {
                    <div class="form-group mb-3">
                        <div class="custom-file">
                            <input type="file" class="custom-file-input" id="FileUpload" name="FileUpload">
                            <label class="custom-file-label" for="FileUpload"></label>
                        </div>
                    </div>
                    <button type="submit" id="Submit" class="btn btn-primary"><i class="fa fa-upload" aria-hidden="true"></i> Yükle</button>
                    <button type="button" id="export" class="btn btn-primary"><i class="fa fa-file-excel-o" aria-hidden="true"></i> Excel'e Aktar</button>
                    <br /><br />
                    <ul id="ulList"></ul>
                }

This is my javascript code:

$('#Myform').ajaxForm({
            beforeSend: function () {
                noFiles();
                $("#ulList").empty();
                $('.progress-bar').width(percentVal);
                loader_icon.show();
            },
            uploadProgress: function (event, position, total, percentComplete) {
                var fp = $("#FileUpload");
                var lg = fp[0].files.length; // get length
                var items = fp[0].files;
                var fragment = "";
                // disable button
                $("#Submit").prop("disabled", true);
                // add spinner to button
                $("#Submit").html(
                    `<span style="@@keyframes spinner-border {
  to { transform: rotate(360deg); }
}
.spinner-border{
    display: inline-block;
    width: 2rem;
    height: 2rem;
    vertical-align: text-bottom;
    border: .25em solid currentColor;
    border-right-color: transparent;
    border-radius: 50%;
    -webkit-animation: spinner-border .75s linear infinite;
    animation: spinner-border .75s linear infinite;
}
.spinner-border-sm{
    height: 1rem;
    border-width: .2em;
}" class="spinner-border spinner-border-sm" role="status" aria-hidden="true"></span> Yükleniyor...`
                );
                if (lg > 0) {

                    fragment += "<li> Veriler yükleniyor..</li>";

                    $("#ulList").append(fragment);
                    for (var i = 0; i < lg; i++) {
                        var i = 0;
                        if (i == 0) {
                            i = 1;
                            var elem = document.getElementById("myBar");
                            var width = 0;
                            var id = setInterval(frame, 100);
                            function frame() {
                                if (width >= 100) {
                                    swal({
                                        title: "Başarılı!",
                                        text: "Kayıt Başarılı!",
                                        type: "success",
                                        showCancelButtonClass: "btn-primary",
                                        confirmButtonText: "OK"
                                    });
                                    clearInterval(id);
                                    i = 0;
                                    elem.style.width = "0%";
                                    $("#Submit").prop("disabled", false);
                                    $("#export").prop("disabled", false);
                                    $("#Submit").html("Yükle");
                                    $("#ulList").empty();
                                    var lastFile =$("#onceki").val();
                                    console.log(lastFile);
                                    var prevFile = $("#oncekii").val();
                                    console.log(prevFile);
                                    $.cookie('oncekiYuklenenDosya', lastFile, { expires: 365 });
                                    $.cookie('dahaOncekiYuklenenDosya', prevFile, { expires: 365 });

                                    @*$.ajax({
                                        type: "GET",
                                        url: '@Url.Action("AddMap","Admin")',
                                        success: function (data) {
                                            console.log(data);
                                        },
                                        error: function (error) {
                                            alert(error);
                                        }
                                    });*@

                                    //var myUrl = "myApiUrl";
                                    //var formData = new FormData();
                                    //formData.append("FileUpload", $("#FileUpload").file);
                                    // $.ajax({
                                    //    type: "POST",
                                    //     url: myUrl,
                                    //     data: formData,
                                    //     crossDomain: true,
                                    //     dataType: 'html',
                                    //     contentType: "multipart/form-data",
                                    //     headers: {
                                    //         //Authorization: "Bearer " + accesstoken,
                                    //         'Access-Control-Allow-Origin': '*'
                                    //     },
                                    //    success: function (data) {
                                    //        alert(data);
                                    //    },
                                    //    error: function (error) {
                                    //        alert(error);
                                    //    }
                                    //});

                                    showMap();

                                } else {
                                    width++;
                                    elem.style.width = width + "%";
                                    elem.innerHTML = width + "%";
                                }
                            }
                        }
                    }
                }
            },
            complete: function (xhr) {
                if (xhr.success == true) {

                    swal({
                        title: "Başarılı!",
                        text: "Kayıt Başarılı!",
                        type: "success",
                        showCancelButtonClass: "btn-primary",
                        confirmButtonText: "OK"
                    });

                }
            }

        });

This is the mvc controller I want to redirect to the web api:

[HttpPost]
    public ActionResult Index(FormCollection formCollection)
    {

            HttpPostedFileBase file = Request.Files["FileUpload"];
            var json = JsonConvert.SerializeObject(
                new
                {
                    files = file,
                    Passed = true,
                    Mesaj = "item added"
                },
                new HttpPostedFileConverter());


        var stringContent = new StringContent(json, Encoding.UTF8, "multipart/form-data");

        try
        {
            using (var client = new HttpClient())
            {
                //var userid = Session["UserID"];
                client.BaseAddress = new Uri("myApiUrl");
                client.DefaultRequestHeaders.Accept.Clear();
                client.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("multipart/form-data"));
                //HttpResponseMessage response = await client.PostAsJsonAsync("api/address/postmap?mapList=" + mapList).Result;
                HttpResponseMessage response = client.PostAsync("api/address/save?FileUpload=", stringContent).Result;

                if (response.IsSuccessStatusCode)
                {
                    return RedirectToAction("Index", "Admin");
                }
                return null;
            }
        }
        catch (Exception e)
        {
            ViewBag.Hata = e.Message;
        }
        return RedirectToAction("Index", "Admin");
    }

This is my asp.net core 3.1 web api controller:

[HttpPost]
    [Route("api/address/save")]
    public async Task<IActionResult> Save(IFormFile FileUpload)
    {
        var userId = _httpContextAccessor.HttpContext.User.FindFirst(ClaimTypes.NameIdentifier).Value;
        var uid = Convert.ToInt32(userId);
        if (FileUpload.Length == 0)
        {
            return BadRequest("please provide valid file");
        }
        var fileName = ContentDispositionHeaderValue
            .Parse(FileUpload.ContentDisposition)
            .FileName
            .TrimStart().ToString();
        var folderName = Request.Form.ContainsKey("folder") ? Request.Form["folder"].ToString() : null;
        bool status;
        using (var fileStream = FileUpload.OpenReadStream())

        using (var ms = new MemoryStream())
        {
            await fileStream.CopyToAsync(ms);
            status = await _awsS3Service.UploadFileAsync(ms, fileName, folderName);

            var mapList = new List<Map>();
            using (var package = new ExcelPackage(ms))
            {
                ExcelWorksheet worksheet = package.Workbook.Worksheets[0];
                var rowCount = worksheet.Dimension.Rows;

                for (int row = 2; row <= rowCount; row++)
                {
                    mapList.Add(new Map 
                    {
                        UserId = uid,
                        Latitude = worksheet.Cells[row, 1].Value.ToString().Trim(),
                        Longitude = worksheet.Cells[row, 2].Value.ToString().Trim(),
                    });
                }
            }
            foreach (var item in mapList)
            {
                dbContext.Map.Add(item);
            }
            dbContext.SaveChanges();
        }
        return status ? Ok("success")
                     : StatusCode((int)HttpStatusCode.InternalServerError, $"error uploading {fileName}");
    }

Where do you think I'm making a mistake? Thank you.

paulsm4
  • 114,292
  • 17
  • 138
  • 190
  • You're mixing lots of different things together. What, exactly, is your *MAIN* question? – paulsm4 Jul 10 '20 at 20:00
  • You are very right, my main problem is not being able to send the file from the controller to the web api. – Emre Şükrü Şentürk Jul 10 '20 at 20:08
  • My second question is not being able to save the data in excel to the sql database after loading excel file in web api. When I try with Postman, I get 500 interval error error. When I try with mvc, I get 400 error. – Emre Şükrü Şentürk Jul 10 '20 at 20:13
  • Q: Have you tried what I suggested: requesting the file(s) and downloading to the server with two handlers (GET and POST) in a single page? Did it work? The main thing (as I see it) is to get *SOMETHING* working quickly; you can always change and expland things afterward. – paulsm4 Jul 12 '20 at 22:17
  • Hello, I tried your suggestion but I was not successful. I need to send the selected excel file to the web api as a parameter, but I could not perform this operation. – Emre Şükrü Şentürk Jul 12 '20 at 23:00
  • My suggestion was .. DON'T START OFF WITH A "web api". Q: Did you try that? What happened? Be specific. Q: If you must have a web api ... then what is your client? Your ASP.Net Core "index" page (with the HTML form)? Or "something else"? Again: the problem is that it seems you're trying to do many things, all at once. I'm trying to get you to focus on ONE thing first. – paulsm4 Jul 13 '20 at 00:43
  • Hello, thank you very much for your interest. Since this project will be uploaded to the server, the operations in mvc must be from api. So I need to capture the file in mvc and send it to web api as a parameter. When I test with Postman, the api uploads the file, but when I test it in mvc I get 400 error. – Emre Şükrü Şentürk Jul 13 '20 at 05:43
  • Your initial post implied *NOTHING* was working. That's why I kept insisting on "Simplifying". Q: So you've got a viable WebAPI controller, and you're able to upload files using Postman, correct? Q: What's your URL in Postman? Q: What exactly happens when you use the *SAME* URL in your HTML form? Use Chrome Developer Tools (or equivalent) to observe the EXACT HTTP request/response (including HTTP headers). ALSO: You keep saying "MVC". Which is it: .Net/ASP.Net MVC 5, or ASP.Net Core 2 or 3? – paulsm4 Jul 13 '20 at 05:46
  • Thank you for the quick response. Yes, I have a suitable controller for web api and it works fine when I send files with postman (except save to database). My MVC project is .Net Framework 4.7.2 asp.net mvc 5 project. The url in postman is the url given by amazon. When I try it in html, I get 400 error. I have the code for my web api controller above. – Emre Şükrü Şentürk Jul 13 '20 at 06:01
  • So your question is *REALLY* just "Q: Why is my ASP.Net MVC 5 form getting an HTTP 400 error?" A: Try adding an [action](https://stackoverflow.com/a/15190991/421195) attribute to your form. – paulsm4 Jul 13 '20 at 14:59
  • Your suggestion is available in my mvc form. My problem has still not been solved. :( – Emre Şükrü Şentürk Jul 13 '20 at 15:19
  • HTTP 400 basically means you're trying to submit your form to a URL that doesn't have a handler configured to receive it. Your original code doesn't have any "action" - that could *DEFINITELY* cause an HTTP 400. Or perhaps now you've changed it ... but it's the *WRONG* action. That can cause an HTTP 400, too. You need the correct a) protocol (http vs. https), b) host, c) port, d) route (fully qualified URL) and e) action (your form is doing a POST; so your controller must have an OnPostAsync handler). Update your question: a) your current code, and b) the successful POSTMAN URL – paulsm4 Jul 13 '20 at 17:00

1 Answers1

1

OK, you have several different questions. The answer(s) involve several different pieces, both server side (your C# ASP.Net Core UI and C# .Net Core controller) and client side (your ASP.Net Core/Razor markup and your Javascript).

Also: I'm using the term "ASP.Net Core" to distinguish it from the older "ASP.Net MVC".

Let's break it down:

  1. MVC View code (I assume this is an ASP.Net Core "Page"): at a glance, looks OK.

  2. javascript code: looks OK. But I'm not sure you need it...

    Q: are you calling this from your MVC View code? If so, how? Could you show us? If not, why not?

  3. mvc controller I want to redirect to the web api: I'm not sure what this is, or if you need it at all.

  4. asp.net core 3.1 web api controller: looks OK...

    ... but I think you can move all this functionality to an ASP.Net Core "Post" handler.

SUGGESTIONS:

  1. Your initial "Index" page handler will look something like this:

https://learn.microsoft.com/en-us/aspnet/core/mvc/models/file-uploads?view=aspnetcore-3.1

index.cshtml:

<form enctype="multipart/form-data" method="post">
    <dl>
        <dt>
            <label asp-for="FileUpload.FormFile"></label>
        </dt>
        <dd>
            <input asp-for="FileUpload.FormFile" type="file">
            <span asp-validation-for="FileUpload.FormFile"></span>
        </dd>
    </dl>
    <input asp-page-handler="Upload" class="btn" type="submit" value="Upload" />
</form>
  1. You'll have a "Post" handler that might look something like this:

https://learn.microsoft.com/en-us/aspnet/core/mvc/models/file-uploads?view=aspnetcore-3.1

index.cshtml.cs:

public async Task<IActionResult> OnPostUploadAsync(List<IFormFile> files)
{
    long size = files.Sum(f => f.Length);

    foreach (var formFile in files)
    {
        if (formFile.Length > 0)
        {
            var filePath = Path.GetTempFileName();

            using (var stream = System.IO.File.Create(filePath))
            {
                await formFile.CopyToAsync(stream);
            }
        }
    }

    // Process uploaded files
    // Don't rely on or trust the FileName property without validation.

    return Ok(new { count = files.Count, size });
}
  1. This will satisfy your first requirement: upload the file to your .Net Core web server.

  2. It really doesn't matter if the file happens to be an Excel spreadsheet, a .jpg image or a text file - you're just uploading a "file".

  3. You can modify your "OnPostUploadAsync()" handler to send the file to an S3 instance, using the AWS SDK for .Net:

    https://docs.aws.amazon.com/AmazonS3/latest/dev/HLuploadFileDotNet.html

  4. You could also modify "OnPostUploadAsync()" to write the file to a SQL database:

    https://www.c-sharpcorner.com/article/upload-files-in-azure-blob-storage-using-asp-net-core/

  5. If you want to be able to choose "save file to server", "upload file to S3", or "write file to MSSQL" at runtime, then

    • Add a radio button or checkbox to your Index.cshtml markup
    • Read the value and call the appropriate method(s) from your "OnPostUploadAsync()" handler.

https://en.wikipedia.org/wiki/ASP.NET_MVC

The ASP.NET MVC is a discontinued web application framework developed by Microsoft, which implements the model–view–controller (MVC) pattern. It is open-source software, apart from the ASP.NET Web Forms component which is proprietary.

ASP.NET Core has since been released, which unified ASP.NET, ASP.NET MVC, ASP.NET Web API, and ASP.NET Web Pages (a platform using only Razor pages). MVC 6 was abandoned due to Core and is not expected to be released. Core is currently planned to merge into .NET 5.

And remember - .Net Core (be it an ASP.Net Core "Page" or a .Net Core "controller") is just an abstraction layer on top of HTTP requests and responses. Ultimately, it's all just HTTP GET and POST. NOT "Controllers", not "Pages".

Yes, you can put your POST request handler into a "controller" (instead of an ASP.Net Core "Page"). All you have to do is 1) Write a new, controller module, 2) use the SAME code I posted above, 3) make sure you define a route to your controller, and 4) Make your <form> use that URL instead of the default.

Easy peasy.

But I urge you to try it my way.

Get it working.

Then, once it's working ... and once you better understand HOW it works, then expand on your solution.

Take "baby steps" toward your solution. Here's a good article on the subject:

https://blog.adrianbolboaca.ro/2013/01/the-history-of-taking-baby-steps/

Don't get discouraged.

Good luck.

paulsm4
  • 114,292
  • 17
  • 138
  • 190
  • Firstly, thanks for your response. I made your instruction for "index.cshtml". But how can I redirect the file I selected in asp.net mvc to the asp.net core web api "OnPostUploadAsync" method? – Emre Şükrü Şentürk Jul 10 '20 at 21:30
  • You don't "redirect a file" - there's no such thing. Specifically, *all* you need to do is 1) use `
    ` in your .cshtml,and 2) have `public async Task OnPostUploadAsync(List files)` handler in your .cshtml.cs. Your original approach was "over complicated": you really didn't need all that stuff. Please "upvote" and "accept" the reply if you found it useful.
    – paulsm4 Jul 11 '20 at 03:48
  • Since this project will be on the server, every process must be from api. So I have to send the file in mvc to api. I am having trouble transmitting from controller to api. – Emre Şükrü Şentürk Jul 11 '20 at 12:27
  • I'm telling you: [*KEEP IT SIMPLE*](https://en.wikipedia.org/wiki/KISS_principle). Please try what I suggested. You can always change things ... *LATER*. Be sure to step through your code in the MSVS debugger and/or Chrome Developer Tools, so you're sure you understand what's going on. Please *TRY* it, and post back what you find. – paulsm4 Jul 11 '20 at 16:35