Tuesday, October 11, 2016

How to upload a file with progress indicator using WEB API

I really wanted to put a very simple example on how you can upload a file using Web API and get a progress indicator on the client. The client code here is using a console application, but I tested the code with portable libraries and it should work just fine. This is not production code! there is no security, not error handling, this is the code I used to prototype and decided to share it, it is a bare bones example.

Server code

Controller code

   public class ValuesController : ApiController
   {
        [HttpGet]
        [Route("upload/movie")]
        public string SayHello()
        {
            return "Hello World";

        }

        /// 
        /// Upload a file 
        /// 
        /// The file information
        [HttpPost]
        [Route("upload/movie")]
        [ResponseType(typeof(List))]
        public async Task SaveFileAttachment()
        {
            List model = await FileUploader.UploadAttachment(Request);
            return Request.CreateResponse(HttpStatusCode.Created, model);
        }


    }

The helper code on the server to upload a file

  public class FileUploader
  {
        const string UPLOAD_PATH = "C:\\UPLOAD";
        public static async Task> UploadAttachment(HttpRequestMessage request)
        {
            
            // Verify that this is a file upload request
            if (!request.Content.IsMimeMultipartContent())
            {
                throw new HttpResponseException(request.CreateResponse(HttpStatusCode.UnsupportedMediaType));
            }

            // Create a stream provider for setting up output streams
            var streamProvider = new MultipartFormDataStreamProvider(UPLOAD_PATH);
            MultipartFormDataStreamProvider resultProvider = null; 

            try
            {
                resultProvider = await request.Content.ReadAsMultipartAsync(streamProvider);
                
            }
            catch(Exception ex)
            {
                var x = ex.Message; // for debugging
                throw ex;
            }
          
            var files = new List();

            // Collect uploaded files
            foreach (MultipartFileData file in resultProvider.FileData)
            {
                string localFileName = Path.GetFileName(file.LocalFileName);
                string originalFileName = file.Headers.ContentDisposition.FileName;
                if (string.IsNullOrEmpty(localFileName) || string.IsNullOrEmpty(originalFileName))
                {
                    continue;
                }

                string fileName = originalFileName.Trim().Trim('"');
                files.Add(new FileUploadModel
                {
                    FileName = fileName,
                    LocalFullPath = Path.Combine(UPLOAD_PATH, localFileName),
                    Comment = String.Empty
                });
            }
            return files;
        }
    }

The client code

Console Application (notice there is a callback here to get the updated status)
 class Program
    {
        static void Main(string[] args)
        {
            string fileName = @"C:\Users\Public\Videos\Sample Videos\Wildlife.wmv";

            FileStream stream = File.Open(fileName, FileMode.Open);
            
            var response = UploadMe.UploadFile(stream, "Wild Life - uploaded.wmv", (progress) => { Console.WriteLine(progress); });
            
            Console.ReadLine();
        }

Client Helper

  public class UploadMe
    {           
        public static async Task> UploadFile(Stream fileStream, string fileName, Action callback)
        {

            ProgressMessageHandler progress = new ProgressMessageHandler();
            progress.HttpSendProgress += new EventHandler((e, args) => { callback(args.ProgressPercentage); });

            MultipartFormDataContent content = new MultipartFormDataContent();

            // the request
            HttpRequestMessage message = new HttpRequestMessage();

            content.Add(new StreamContent(fileStream), "file", fileName);


            message.Method = HttpMethod.Post;
            message.Content = content;
            message.RequestUri = new Uri("http://localhost/UploadFileNoSecurity/upload/movie");


            var client = HttpClientFactory.Create(progress);
            client.Timeout = TimeSpan.FromHours(1);

            var response = await client.SendAsync(message);

            if (response.IsSuccessStatusCode)
            {
                Task httpResponse = response.Content.ReadAsStringAsync();
                List json = JsonConvert.DeserializeObject>(httpResponse.Result);
                return json;

            }

            return null;            
        }

    }

The DTO code

namespace UploadModel
{
    public class FileUploadModel
    {
     
        public string FileName { get; set; }

     
        public string LocalFullPath { get; set; }

     
        public string Comment { get; set; }
    }
}

No comments: