1

I have a controller link to an input element for uploading. In my controller I am getting a weird error that I do not quite understand. Severity Code Description Project Path File Line Suppression State

Error CS1061 'HttpRequestMessage' does not contain a definition for 'Files' and no accessible extension method 'Files' accepting a first argument of type 'HttpRequestMessage' could be found (are you missing a using directive or an assembly reference?) SimSentinel C:\Users\tsach\Source\Workspaces\SIMSentinelv2\Website\SimSentinel\SimSentinel\Controllers C:\Users\tsach\Source\Workspaces\SIMSentinelv2\Website\SimSentinel\SimSentinel\Controllers\BulkSMSUploadController.cs

    using System.Data;
    using System.Linq;
    using System.Web.Http;
    using System.Web.Security;
    using Repositories.Interfaces;
    using Repositories.Interfaces.Dtos;
    using SimSentinel.Models;
    using System;
    using System.Text.RegularExpressions;
    using Newtonsoft.Json.Linq;
    using Newtonsoft.Json.Schema;
    using System;
    using System.Collections.Generic;
    using System.IO;
    using System.Linq;
    using System.Web;
    using System.Web.Mvc;
    //using System.Web.Http.HttpPut;


    namespace SimSentinel.Controllers
    {
        [System.Web.Http.Authorize]
        public class BulkSMSUploadController : ApiController
        {
          public ActionResult Index()
          {
             //ViewBag.Message = "Modify this template to jump-start your ASP.NET MVC application.";

             //return View();
             return null; 
          }

          [System.Web.Mvc.HttpPost]
          public ActionResult UploadFiles()
          {
             if (Request.Files.Count <= 0)
             {
                return Json("No files selected.");
             }
             else
             {
                try
                {
                   HttpFileCollectionBase files = Request.Files;
                   for (int i = 0; i < files.Count; i++)
                   {
                      string path = AppDomain.CurrentDomain.BaseDirectory + "Uploads/";
                      string filename = Path.GetFileName(Request.Files[i].FileName);

                      HttpPostedFileBase file = files[i];
                      string fname;
                      if (Request.Browser.Browser.ToUpper() == "IE" || Request.Browser.Browser.ToUpper() == "INTERNETEXPLORER")
                      {
                         string[] testfiles = file.FileName.Split(new char[] { '\\' });
                         fname = testfiles[testfiles.Length - 1];
                      }
                      else
                      {
                         fname = file.FileName;
                      }

                      fname = Path.Combine(Server.MapPath("~/Uploads/"), fname);
                      file.SaveAs(fname);
                   }

                   return Json("File Uploaded Successfully!");
                }
                catch (Exception ex)
                {
                   return Json("Error occurred. Error details: " + ex.Message);
                }
             }
          }

          //public ActionResult About()
          //{
          //   ViewBag.Message = "Your app description page.";

          //   return View();
          //}
       }
    }

So after all this I have adjusted my controller. See code below which works however it redirects to the actual controller and that is a problem in a SPA application. In addition, the file also saves in a wierd format almost like a randomly generated string like BodyPart_2ea18b56-0c11-41f6-81ff-204bb377cbbf

using System.Diagnostics;
using System.Net;
using System.Net.Http;
using System.Threading.Tasks;
using System.Web;
using System.Web.Http;

public class Upload2Controller : ApiController
{
   public async Task<HttpResponseMessage> PostFormData()
   {
      // Check if the request contains multipart/form-data.
      if (!Request.Content.IsMimeMultipartContent())
      {
         throw new HttpResponseException(HttpStatusCode.UnsupportedMediaType);
      }

      string root = HttpContext.Current.Server.MapPath("~/Files");
      var provider = new MultipartFormDataStreamProvider(root);

      try
      {
         // Read the form data.
         await Request.Content.ReadAsMultipartAsync(provider);

         // This illustrates how to get the file names.
         foreach (MultipartFileData file in provider.FileData)
         {
            Trace.WriteLine(file.Headers.ContentDisposition.FileName);
            Trace.WriteLine("Server file path: " + file.LocalFileName);
         }
         return Request.CreateResponse(HttpStatusCode.OK);
      }
      catch (System.Exception e)
      {
         return Request.CreateErrorResponse(HttpStatusCode.InternalServerError, e);
      }
   }

}
Zidane
  • 1,696
  • 3
  • 21
  • 35

2 Answers2

3

You should be careful on following steps.

  • Make sure your form html element has enctype = "multipart/form-data" So it should be something like <form action="someaction" enctype = "multipart/form-data"> </form>

I usually use html helper..

@using (Html.BeginForm("Index", "JobApplication", FormMethod.Post, new { @enctype = "multipart/form-data", @id = "myForm", @class = "form-horizontal" }))
    {
// your input datas
}
  • If you would like to post image or some document, insead of using request, you should use public HttpPostedFileBase File { get; set; } in your ViewModel. Then you can easily use it on your razor.

    @Html.TextBoxFor(m => m.File, new { @type = "file", @onchange = "SomeValidationOnClientSide(this);"})

On the backend side you can validate your case. In my case I only accept PDF files..

if ((from file in model.Files where file != null select file.FileName.Split('.')).Any(arr => arr[arr.Length - 1].ToLower() != "pdf"))
        {
            ModelState.AddModelError(string.Empty, "We only accept PDF files!");
            return View(model);
        }
        if (model.Files.Count() > 2)
        {
            ModelState.AddModelError(string.Empty,
                "You have exceeded maximum file upload size. You can upload maximum 2 PDF file!");
            return View(model);
        }

Edit: I see that you implemented ApiController instead of Contoller. So I understand that you are developing WEB.API, you should add this into question tags as well.

If you would like to develop ApiController, you should send byte[] and process this byte[] into your apiController.

Mehmet Taha Meral
  • 3,315
  • 28
  • 30
1

From what I see your controller implements Web API controller (i.e. using ApiController.Request), which has definition as shown below:

public System.Net.Http.HttpRequestMessage Request { get; set; }

The return type is HttpRequestMessage which doesn't have Files property, as opposed to intended HttpRequestBase that implemented as return type of Controller.Request property below:

public System.Web.HttpRequestBase Request { get; }

To solve this issue, you need to inherit from System.Web.Mvc.Controller base class and move Web API requests to another class which inherits ApiController, because you cannot inherit both System.Web.Mvc.Controller and System.Web.Http.ApiController on the same class:

namespace SimSentinel.Controllers
{
   public class BulkSMSUploadController : Controller
   {
      [System.Web.Mvc.HttpPost]
      public ActionResult UploadFiles()
      {
         if (Request.Files.Count <= 0)
         {
            return Json("No files selected.");
         }
         else
         {
            try
            {
               HttpFileCollectionBase files = Request.Files;
               for (int i = 0; i < files.Count; i++)
               {
                  string path = AppDomain.CurrentDomain.BaseDirectory + "Uploads/";
                  string filename = Path.GetFileName(Request.Files[i].FileName);

                  HttpPostedFileBase file = files[i];
                  string fname;
                  if (Request.Browser.Browser.ToUpper() == "IE" || Request.Browser.Browser.ToUpper() == "INTERNETEXPLORER")
                  {
                     string[] testfiles = file.FileName.Split(new char[] { '\\' });
                     fname = testfiles[testfiles.Length - 1];
                  }
                  else
                  {
                     fname = file.FileName;
                  }

                  fname = Path.Combine(Server.MapPath("~/Uploads/"), fname);
                  file.SaveAs(fname);
               }

               return Json("File Uploaded Successfully!");
            }
            catch (Exception ex)
            {
               return Json("Error occurred. Error details: " + ex.Message);
            }
         }
      }
   }

   [System.Web.Http.Authorize]
   public class BulkSMSUploadWebApiController : ApiController
   {
       public IHttpActionResult Index()
       {
           return null; 
       }
   }
}

If you want to upload file using Web API controller, you should use HttpResponseMessage to retrieve file details with MultipartFileData as provided in this example (make sure you're checking against IsMimeMultipartContent first).

Tetsuya Yamamoto
  • 24,297
  • 8
  • 39
  • 61
  • Thank you all for your help I have had to adjust the code in the controller to get it to work. So the file is now saving but I have run into two extra problems. The file saved with some random string like BodyPart_2ea18b56-0c11-41f6-81ff-204bb377cbbf and after saving redirects to the actual controller itself and this is a problem in SPA application because it means the user has to login again everytime after uploading a file – Zidane Oct 30 '18 at 09:10
  • I will update my post now to show you what my new controller looks like – Zidane Oct 30 '18 at 09:11
  • `2ea18b56-0c11-41f6-81ff-204bb377cbbf` seems like a GUID-formatted string (as 8-4-4-4-12 format). I suggest you check `file.Headers.ContentDisposition.FileName` and `file.LocalFileName` to find out where the additional GUID comes from. – Tetsuya Yamamoto Oct 30 '18 at 09:36