5
votes

I need to allow image upload in my AngularJS + WebAPI project. to achieve this I am using ng-file-upload according to this sample code: http://monox.mono-software.com/blog/post/Mono/233/Async-upload-using-angular-file-upload-directive-and-net-WebAPI-service/

with a few adjustments to the post code to look like this:

$scope.onFileSelect = function ($files) {
    console.log("on file select is running!");
    //$files: an array of files selected, each file has name, size, and type.
    for (var i = 0; i < $files.length; i++) {
        var $file = $files[i];
        (function (index) {
            Upload.upload({
                url: "/api/Uploads/Upload", // webapi url
                method: "POST",                   
                file: $file
            }).progress(function (evt) {
                // get upload percentage
                console.log('percent: ' + parseInt(100.0 * evt.loaded / evt.total));
            }).success(function (data, status, headers, config) {
                // file is uploaded successfully
                console.log(data);
            }).error(function (data, status, headers, config) {
                // file failed to upload
                console.log(data);
            });
        })(i);
    }
}

I have quite a few web API controllers already and I added a new one according to the code sample in the link above (that inherits from System.web.http.ApiController instead of the "regular" Microsoft.AspNet.Mvc.Controller class):

 [Route("api/[controller]")]
public class UploadsController : ApiController
{
    [HttpPost] // This is from System.Web.Http, and not from System.Web.Mvc        
    public async Task<HttpResponseMessage> Upload()
    {
        if (!Request.Content.IsMimeMultipartContent())
        {
            this.Request.CreateResponse(HttpStatusCode.UnsupportedMediaType);
        }

        var provider = GetMultipartProvider();
        var result = await Request.Content.ReadAsMultipartAsync(provider);

        // On upload, files are given a generic name like "BodyPart_26d6abe1-3ae1-416a-9429-b35f15e6e5d5"
        // so this is how you can get the original file name
        var originalFileName = GetDeserializedFileName(result.FileData.First());

        // uploadedFileInfo object will give you some additional stuff like file length,
        // creation time, directory name, a few filesystem methods etc..
        var uploadedFileInfo = new FileInfo(result.FileData.First().LocalFileName);



        // Through the request response you can return an object to the Angular controller
        // You will be able to access this in the .success callback through its data attribute
        // If you want to send something to the .error callback, use the HttpStatusCode.BadRequest instead
        var returnData = "ReturnTest";
        return this.Request.CreateResponse(HttpStatusCode.OK, new { returnData });
    }

The problem is, i keep getting "404 not found" when posting.

I tried:

  1. all stack overflow answers

  2. online answers

  3. removing the content of the "Upload" function, and changing to MVC's Controller base class -> still same result.

  4. changing the name of "Upload" method to "Post" and posting to "/api/uploads" -> same 404.

Please help! thanks!

EDIT

this is the browser "Network" tab: All previous posts succeeded, just the upload fails

  • I am using HTTPS

  • this was tested "live" (without localhost) = same result.

EDIT2

my routes:

  app.UseMvc(routes =>
        {
            routes.MapRoute(
                name: "default",
                template: "{controller=Home}/{action=Index}/{id?}");
        });

these are the only routes that are declared.

EDIT3

I am 100% sure the problem is me using "ApiController" as base class instead of "Controller". i added a new controller and i can access it no problem. now just making it work as i don't have " Request.Content " in "Controller" - any ideas?

I see 2 possibilities to make it work:

  1. pass HttpRequestMessage as a parameter to the method:

    public async Task Post([FromBody]HttpRequestMessage request)

but i am getting HTTP 500 as i don't know how to pass from angular's POST.

or:

  1. get Request.Content to resolve under Controller (no idea how).

has someone managed to do this? thanks!

2
anyone? i am really stuck here.... - tarkil
You tried all Stack Overflow answers already? Wow. – If there’s a 404, then the server rejects the request. Try to post to that URL with a normal XMLHttpRequest to see if the server is responding properly. Once you get that working, replace it by an actual file upload. - poke
@poke i mean related answers. the post target is the same as all my other post "/api/[contoller]" - why should it reject this one? and also how can i send a normal XMLHttpRequest request? thanks! - tarkil
If every other endpoint works fine, then that’s even more odd, so you should definitely check to see if the request is working properly. See this for information on how to use XHR. Also, you should try debugging in C# to see if the request arrives and what causes the failure. And be sure to check your browser’s network development tools as well, to see if the URL is really correct. - poke
@poke when trying to debug, even in the constructor of the web API controller, it doesn't reach it, thus maybe a routing problem? or am i wrong to expect the constructor to run at this point? - tarkil

2 Answers

2
votes

I was able to solve the problem i posted in EDIT3 by using:

 if (Request.Form.Files != null && Request.Form.Files.Count > 0)
        {
            var file = Request.Form.Files[0];
            var contentType = file.ContentType;

            using (var fileStream = file.OpenReadStream())
            {
                using (var memoryStream = new MemoryStream())
                {
                    await fileStream.CopyToAsync(memoryStream);
                    FileStream newFS = new FileStream(Settings.UserImagesDir + "\\name.png", FileMode.Create);
                    memoryStream.WriteTo(newFS);
                    newFS.Close();                     
                }
            }
        }

and all this in a regular web api controller that inherits from Controller.

thanks everyone for your help, although no one solved it you all pushed me in the right direction.

2
votes

The problem is that your routing is misconfigured for the URL /api/Uploads/Upload. In order to make it match, you have to specify a template for the method Upload, since it is not actually named after an HTTP method.

[HttpPost("upload")]
public async Task<HttpResponseMessage> Upload()
{
    // code omitted...
}