Web API File Upload, Single or Multiple files

This article demonstrates file uploads using Web API 2. The examples use the MultipartFormDataStreamProvider class. The controller implementation saves the files in memory first, so for large files or with a lot of HTTP traffic, this is not a good solution. Links to other examples are also included at the bottom of this post.

Code: https://github.com/damienbod/WebApiFileUpload

The Web API controller implements the server method for file upload. This saves the content to a provider location and creates a new file name with no endings. The file(s) information is collected from the form data and the file headers. The headers are not always set depending on the client. This server method works for both single and multiple file uploads.

using System;
using System.IO;
using System.Linq;
using System.Net;
using System.Net.Http;
using System.Threading.Tasks;
using System.Web.Http;

namespace WebAPIDocumentationHelp.Controllers
{
    [RoutePrefix("api/test")]
    public class FileUploadController : ApiController
    {
        private static readonly string ServerUploadFolder = "C:\\Temp"; //Path.GetTempPath();

        [Route("files")]
        [HttpPost]
        [ValidateMimeMultipartContentFilter]
        public async Task<FileResult> UploadSingleFile()
        {
            var streamProvider = new MultipartFormDataStreamProvider(ServerUploadFolder);
            await Request.Content.ReadAsMultipartAsync(streamProvider);
   
            return new FileResult
            {
                FileNames = streamProvider.FileData.Select(entry => entry.LocalFileName),
                Names = streamProvider.FileData.Select(entry => entry.Headers.ContentDisposition.FileName),
                ContentTypes = streamProvider.FileData.Select(entry => entry.Headers.ContentType.MediaType),
                Description = streamProvider.FormData["description"],
                CreatedTimestamp = DateTime.UtcNow,
                UpdatedTimestamp = DateTime.UtcNow, 
                DownloadLink = "TODO, will implement when file is persisited"
            };
        }
    }
}

The server method uses a ValidateMimeMultipartContentFilter attribute to check if the correct MIME type is sent. This can then be used for all uploads methods.

using System.Net;
using System.Net.Http;
using System.Web.Http;
using System.Web.Http.Controllers;
using System.Web.Http.Filters;

namespace WebAPIDocumentationHelp.Controllers
{
    public class ValidateMimeMultipartContentFilter : ActionFilterAttribute
    {
        public override void OnActionExecuting(HttpActionContext actionContext)
        {
            if (!actionContext.Request.Content.IsMimeMultipartContent())
            {
                throw new HttpResponseException(HttpStatusCode.UnsupportedMediaType);
            }
        }

        public override void OnActionExecuted(HttpActionExecutedContext actionExecutedContext)
        {

        }

    }
}

If the upload is successful, a FileResult object is sent back to the client. This object in the demo contains more information than is required. This could be used to store the files in a database and a link with the file name could be returned to the client instead.

using System;
using System.Collections.Generic;

namespace WebAPIDocumentationHelp.Controllers
{
    public class FileResult
    {
        public IEnumerable<string> FileNames { get; set; }
        public string Description { get; set; }
        public DateTime CreatedTimestamp { get; set; }
        public DateTime UpdatedTimestamp { get; set; }
        public string DownloadLink { get; set; }
        public IEnumerable<string> ContentTypes { get; set; }
        public IEnumerable<string> Names { get; set; }
    }
}

This is the html example used for a single file upload.

<html>
<head>
    <meta name="viewport" content="width=device-width" />
    <title>WebApiTest</title>
</head>
    <body>
        <div>
        <form enctype="multipart/form-data" method="post" action="http://localhost:8081/api/test/files" id="ajaxUploadForm" novalidate="novalidate">

            <fieldset>
                <legend>Upload Form</legend>
                <ol>
                    <li>
                        <label>Description </label>
                        <input type="text" style="width:317px" name="description" id="description">
                    </li>
                    <li>
                        <label>Upload </label>
                        <input type="file" style="width:317px" name="file" id="fileInput">                   
                    </li>
                    <li>
                        <input type="submit" value="Upload" id="ajaxUploadButton" class="btn">
                    </li>
                </ol>
            </fieldset>
        </form>
    </div>
</body>
</html>

This is the html file used for a multiple file upload. It is important that the name is supplied in the multiple input control, otherwise the server cannot separate the files.

<!doctype html>
<html>
    <head>
        <title>Test</title>
    </head>
    <body>
        <form enctype="multipart/form-data" method="post" action="http://localhost:8081/api/test/files" id="ajaxUploadForm" novalidate="novalidate">

            <fieldset>
                <legend>Upload Form</legend>
                <ol>
                    <li>
                        <label>Description </label>
                        <input type="text" style="width:317px" name="description" id="description">
                    </li>
                    <li>
                        <label>upload </label>
                        <input type="file" id="fileInput" name="fileInput" multiple>
                    </li>
                    <li>
                        <input type="submit" value="Upload" id="ajaxUploadButton" class="btn">
                    </li>
                </ol>
            </fieldset>
        </form>
    </body>
</html>

Here’s an html client example for a multiple file upload:

MultiClientFileupload01

And the reults of the file upload in XML format.

MultiClientFileupload02

What’s missing:

The example saves the requests in memory. It would be better if this was not required for performance. The files usually need to be saved to a database. This works easy, if using SQL 2012 as the files can just be saved like above using FileTables. But if using SQLite or something else, the files need to be read again and saved to the database, it would be better if the files were streamed directly to the database and not first to a provider.

Links:

http://marcinbudny.blogspot.com.au/2014/02/sending-binary-data-along-with-rest-api.html

http://www.strathweb.com/2012/08/a-guide-to-asynchronous-file-uploads-in-asp-net-web-api-rtm/

http://www.strathweb.com/2012/09/dealing-with-large-files-in-asp-net-web-api/

http://www.piotrwalat.net/file-download-service-with-resume-support-using-asp-net-web-api/

http://www.asp.net/web-api/overview/working-with-http/sending-html-form-data,-part-2

http://hintdesk.com/android-upload-files-to-asp-net-web-api-service/

http://www.dotnettips.info/post/920

http://www.dotnettips.info/post/1132

http://www.c-sharpcorner.com/UploadFile/2b481f/upload-files-in-and-download-files-from-database-in-web-api/

12 comments

  1. How do you keep the file extension and name ?

    1. Hi Djens, thanks for your coment.

      It’s in the ContentType or Filename. I usually save this to a separate Table or create a View in the database as well as the Files. Then I can select or search quickly and only load the file when really required. Here’s an example

      https://damienbod.wordpress.com/2014/04/08/web-api-file-upload-with-ms-sql-server-filetable/

      greetings Damien

  2. […] use the MultipartFormDataStreamProvider class. The controller implementation saves the … Download Web API File Upload, Single or Multiple files | Software … | […]

  3. Reblogged this on denhul.

  4. […] or binaries can also be sent to Web API methods. The article demonstrates how to do […]

  5. How does it work when uploading multiple files?

    1. Hi Mark

      Server side, the MultipartFormDataStreamProvider makes this possible

      https://msdn.microsoft.com/en-us/library/system.net.http.multipartformdatastreamprovider%28v=vs.118%29.aspx

      Client input with the multiple attribute

      http://www.w3schools.com/tags/att_input_multiple.asp

      greetings Damien

  6. Under “What’s Missing” you say.. “The example saves the requests in memory.” meaning what? You are not saving in memory nor extracting the file uploads into memory. You are saving any uploads into it’s own file. Is there any way to NOT save into a file, and just work in-memory only server-side (before writing to a database) ?

  7. Good post, If anyone stumbles upon this and needs jQuery ajax with web api file upload check File upload using web api and jquery ajax
    thanks

    1. Hi Vikas nice, maybe add some Anti-forgery protection, greetings Damien

      1. Excellent suggestion, yes it is better to increase security using Anti-Forgery token and it is possible I think.
        Thanks

Leave a reply to tutorialzine login | PDF Finder Cancel reply

This site uses Akismet to reduce spam. Learn how your comment data is processed.