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/

8 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

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s

%d bloggers like this: