83

I am trying to figure this out. I was not getting any useful error messages with my code so I used something else to generate something. I have attached that code after the error message. I have found a tutorial on it but I do not know how to implement it with what I have. This is what I currently have:

public async Task<object> PostFile()
    {
        if (!Request.Content.IsMimeMultipartContent())
            throw new Exception();


        var provider = new MultipartMemoryStreamProvider();
        var result = new { file = new List<object>() };
        var item = new File();

        item.CompanyName = HttpContext.Current.Request.Form["companyName"];
        item.FileDate = HttpContext.Current.Request.Form["fileDate"];
        item.FileLocation = HttpContext.Current.Request.Form["fileLocation"];
        item.FilePlant = HttpContext.Current.Request.Form["filePlant"];
        item.FileTerm = HttpContext.Current.Request.Form["fileTerm"];
        item.FileType = HttpContext.Current.Request.Form["fileType"];

        var manager = new UserManager<ApplicationUser>(new UserStore<ApplicationUser>(new ApplicationDbContext()));
        var user = manager.FindById(User.Identity.GetUserId());

        item.FileUploadedBy = user.Name;
        item.FileUploadDate = DateTime.Now;

        await Request.Content.ReadAsMultipartAsync(provider)
         .ContinueWith(async (a) =>
         {
             foreach (var file in provider.Contents)
             {
                 if (file.Headers.ContentLength > 1000)
                 {
                     var filename = file.Headers.ContentDisposition.FileName.Trim('\"');
                     var contentType = file.Headers.ContentType.ToString();
                     await file.ReadAsByteArrayAsync().ContinueWith(b => { item.FilePdf = b.Result; });
                 }


             }


         }).Unwrap();

        db.Files.Add(item);
        db.SaveChanges();
        return result;

    }

Error:

Object {message: "The request entity's media type 'multipart/form-data' is not supported for this resource.", exceptionMessage: "No MediaTypeFormatter is available to read an obje…om content with media type 'multipart/form-data'.", exceptionType: "System.Net.Http.UnsupportedMediaTypeException", stackTrace: " at System.Net.Http.HttpContentExtensions.ReadAs…atterLogger, CancellationToken cancellationToken)"}exceptionMessage: "No MediaTypeFormatter is available to read an object of type 'HttpPostedFileBase' from content with media type 'multipart/form-data'."exceptionType: "System.Net.Http.UnsupportedMediaTypeException"message: "The request entity's media type 'multipart/form-data' is not supported for this resource."stackTrace: " at System.Net.Http.HttpContentExtensions.ReadAsAsync[T](HttpContent content, Type type, IEnumerable1 formatters, IFormatterLogger formatterLogger, CancellationToken cancellationToken) ↵ at System.Net.Http.HttpContentExtensions.ReadAsAsync(HttpContent content, Type type, IEnumerable1 formatters, IFormatterLogger formatterLogger, CancellationToken cancellationToken)

Code used to generate error message:

    [HttpPost]
    public string UploadFile(HttpPostedFileBase file)
    {

        if (file.ContentLength > 0)
        {
            var fileName = Path.GetFileName(file.FileName);
            var path = Path.Combine(HttpContext.Current.Server.MapPath("~/uploads"), fileName);
            file.SaveAs(path);


        }
        return "/uploads/" + file.FileName;
    }

Class:

public class File
{
    public int FileId { get; set; }
    public string FileType { get; set; }
    public string FileDate { get; set; }
    public byte[] FilePdf { get; set; }
    public string FileLocation { get; set; }
    public string FilePlant { get; set; }
    public string FileTerm { get; set; }
    public DateTime? FileUploadDate { get; set; }
    public string FileUploadedBy { get; set; }

    public string CompanyName { get; set; }
    public virtual ApplicationUser User { get; set; }
}
3
  • So it looks like you're POST-ing data from one backend to another. Any reason you're using multipart/form rather than JSON or raw data? Commented Feb 6, 2015 at 16:10
  • no reason, it was what worked for me a while back. I posted my angular. I open for suggestions Commented Feb 6, 2015 at 16:13
  • 4
    @timothyclifford Why not? Multipart is an efficient way to do this. The point of this QA page is not suggesting another way to do it instead of fixing the problem you have with the approach you are looking for, Commented Apr 13, 2020 at 12:08

10 Answers 10

116

I normally use the HttpPostedFileBase parameter only in Mvc Controllers. When dealing with ApiControllers try checking the HttpContext.Current.Request.Files property for incoming files instead:

[HttpPost]
public string UploadFile()
{
    var file = HttpContext.Current.Request.Files.Count > 0 ?
        HttpContext.Current.Request.Files[0] : null;

    if (file != null && file.ContentLength > 0)
    {
        var fileName = Path.GetFileName(file.FileName);

        var path = Path.Combine(
            HttpContext.Current.Server.MapPath("~/uploads"),
            fileName
        );

        file.SaveAs(path);
    }

    return file != null ? "/uploads/" + file.FileName : null;
}
Sign up to request clarification or add additional context in comments.

9 Comments

this is the errror message. Could not find a part of the path 'C:\Development\Transp\temp\uploads\02-Unicare.PNG'.
the permissions are set just by using the windows file explorer?
Now it seems the path you're using to save the received file is invalid. Check that the folder exists, and that your application has read/write permission on that folder (yes, you can do that using windows explorer).
i changed the folder to App_Start and it worked! now this is actually storing the file on the server. If i do it this way how can i store the path and other properties in the database?
I'm glad it worked. Regarding your new doubt, instead of updating this question, I suggest you create a new question here in StackOverflow showing how you're trying to save things in the database, and why it isn't working.
|
79

This is what solved my problem
Add the following line to WebApiConfig.cs

config.Formatters.XmlFormatter.SupportedMediaTypes.Add(new System.Net.Http.Headers.MediaTypeHeaderValue("multipart/form-data"));

1 Comment

This only makes ASP.NET route multipart/form-data requests to your controllers. It won't be able to deserialize the form values and bind them to your method's parameters, because the XmlFormatter can't parse the multipart/form-data payload.
22

You can use something like this

[HttpPost]
public async Task<HttpResponseMessage> AddFile()
{
    if (!Request.Content.IsMimeMultipartContent())
    {
        this.Request.CreateResponse(HttpStatusCode.UnsupportedMediaType);
    }

    string root = HttpContext.Current.Server.MapPath("~/temp/uploads");
    var provider = new MultipartFormDataStreamProvider(root);
    var result = await Request.Content.ReadAsMultipartAsync(provider);

    foreach (var key in provider.FormData.AllKeys)
    {
        foreach (var val in provider.FormData.GetValues(key))
        {
            if (key == "companyName")
            {
                var companyName = val;
            }
        }
    }

    // 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());

    var uploadedFileInfo = new FileInfo(result.FileData.First().LocalFileName);
    string path = result.FileData.First().LocalFileName;

    //Do whatever you want to do with your file here

    return this.Request.CreateResponse(HttpStatusCode.OK, originalFileName );
}

private string GetDeserializedFileName(MultipartFileData fileData)
{
    var fileName = GetFileName(fileData);
    return JsonConvert.DeserializeObject(fileName).ToString();
}

public string GetFileName(MultipartFileData fileData)
{
    return fileData.Headers.ContentDisposition.FileName;
}

13 Comments

Can you please show me what I need to do here. I don't quite understand
The above code will stay in your Web API Controller that accepts multipart/form-data. Once the code verifies its a multipart content we get the file and extra data like "companyname", ... and you can process your file, save it and return the desired result. You can use angular/.net code to access this method
I plugged in what you have to see what happens but it throws the error at the first foreach loop. part of the issue is i dont quite understand how it is being converted to binary format. I created the tmp folder and made sure it has write permissions
FormData could be null try this instead result.FormData.GetValues("companyname").FirstOrDefault();
same error with : foreach (var val in result.FormData.GetValues("companyname").FirstOrDefault())
|
17

5 years later on and .NET Core 3.1 allows you to do specify the media type like this:

[HttpPost]
[Consumes("multipart/form-data")]
public IActionResult UploadLogo()
{
    return Ok();
}

4 Comments

Actually, in .net core, passing an IFormFile as argument to the controller seems to be enough for .net to allow multipart/form-data (tested with .net 5).
@GyumFox Not really, I'm now testing in .NET 5 and getting blocked because of this.
Dunno, this works for me: [HttpPost] public FileResult Post(IFormFile file, string otherArg)
How can I setup in visuaBasic .net api web? <Consumes("multipart/form-data")> not work
12

Perhaps it is late for the party. But there is an alternative solution for this is to use ApiMultipartFormFormatter plugin.

This plugin helps you to receive the multipart/formdata content as ASP.NET Core does.

In the github page, demo is already provided.

3 Comments

When I do send properties but do not the actual file to the API controller's action, serialization is not happening, I am receiving null as my model. If I do send a file or remove file property from model, serialization works fine. Is it as designed or I am missing something?
@Roman.Pavelko, can you create a minimal reproduction on git, it will be better to know what really happens :)
Thanks. This saved me for this question: stackoverflow.com/questions/68188224/…
2

Here's another answer for the ASP.Net Core solution to this problem...

On the Angular side, I took this code example...

https://stackblitz.com/edit/angular-drag-n-drop-directive

... and modified it to call an HTTP Post endpoint:

  prepareFilesList(files: Array<any>) {

    const formData = new FormData();
    for (var i = 0; i < files.length; i++) { 
      formData.append("file[]", files[i]);
    }

    let URL = "https://localhost:44353/api/Users";
    this.http.post(URL, formData).subscribe(
      data => { console.log(data); },
      error => { console.log(error); }
    );

With this in place, here's the code I needed in the ASP.Net Core WebAPI controller:

[HttpPost]
public ActionResult Post()
{
  try
  {
    var files = Request.Form.Files;

    foreach (IFormFile file in files)
    {
        if (file.Length == 0)
            continue;
        
        string tempFilename = Path.Combine(Path.GetTempPath(), file.FileName);
        System.Diagnostics.Trace.WriteLine($"Saved file to: {tempFilename}");

        using (var fileStream = new FileStream(tempFilename, FileMode.Create))
        {
            file.CopyTo(fileStream);
        }
    }
    return new OkObjectResult("Yes");
  }
  catch (Exception ex)
  {
    return new BadRequestObjectResult(ex.Message);
  }
}

Shockingly simple, but I had to piece together examples from several (almost-correct) sources to get this to work properly.

Comments

1

For Asp.net MVC I need to validate and process before saving the file so I solve my problem by some more steps to give the file to a property. At first

HttpContext.Current.Request.Files[i]

is HttpPostedFile so I made a property with this type (I have multiple files and need more properties) so I assign file to property like below

if (HttpContext.Current.Request.Files.Count>0)
        {

            var files = HttpContext.Current.Request.Files;
            for (int i = 0; i < files.Keys.Count; i++)
            {
                if (files.Keys[i].ToLower() == "ShenasnameImage".ToLower())
                {
                    model.ShenasnameImage = files[i];
                    
                }
                if (files.Keys[i].ToLower() == "MelliCardImage".ToLower())
                {
                    model.MelliCardImage = files[i];
                    
                }
            }
        }

then I make validation and process for file properties and finally save files.

Comments

0

For me worked this way, hope it helps!

        [HttpPost("AddProduct")]

        public async Task<IActionResult> AddProduct( IFormFile files)
        {
            string fileGuid = Guid.NewGuid().ToString();
            var pathImage = Path.Combine(_hostEnvironment.ContentRootPath, "Images", fileGuid);
            var streamImage = new FileStream(pathImage, FileMode.Append);
            await files.CopyToAsync(streamImage);

            return Ok();

           
        }

1 Comment

Try indenting the code
-2

check ur WebApiConfig and add this

GlobalConfiguration.Configuration.Formatters.XmlFormatter.SupportedMediaTypes.Clear();

2 Comments

Why would removing supported media types suddenly allow one?
This answer makes no sense, and it doesn't even work.
-5

You're getting HTTP 415 "The request entity's media type 'multipart/form-data' is not supported for this resource." because you haven't mention the correct content type in your request.

1 Comment

care to add more explanation? I am not sure how to resolve this.

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.