Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to upload file in chunks in ASP.NET using ng-Flow

I am trying to implement ng-flow https://github.com/flowjs/ng-flow for file upload. It upload files in chunk, I successfully set this on client but I am not sure how to handle file on backend inside web api method.

  public void Upload()
        {
            //how to handle file?
        }

The request contain the following information enter image description here

like image 784
Ammar Khan Avatar asked Jun 29 '14 12:06

Ammar Khan


2 Answers

Here's the ASP.NET Web API controller that I've come up with to save the chunks and assemble them.

using System;
using System.Diagnostics;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using System.Net;
using System.Net.Http;
using System.IO;
using System.Web;
using System.Web.Http;

namespace Api.Controllers
{
    [RoutePrefix("api")]
    public class FlowUploadController : ApiController
    {

        string root = Path.Combine(Path.GetTempPath(), "FlowUpload");

        [Route("flowupload"), AcceptVerbs("GET")]
        public object Upload(
            int flowChunkNumber,
            string flowIdentifier)
        {
            if (ChunkIsHere(flowChunkNumber, flowIdentifier))
            {
                return Request.CreateResponse(HttpStatusCode.OK);
            }
            else
            {
                return Request.CreateResponse(HttpStatusCode.NotFound);
            }
        }

        [Route("flowupload"), AcceptVerbs("POST")]
        public async Task<object> Upload()
        {
            // Check if the request contains multipart/form-data.
            if (!Request.Content.IsMimeMultipartContent())
            {
                throw new HttpResponseException(HttpStatusCode.UnsupportedMediaType);
            }
            if (!Directory.Exists(root)) Directory.CreateDirectory(root);
            var provider = new MultipartFormDataStreamProvider(root);
            try
            {
                await Request.Content.ReadAsMultipartAsync(provider);
                int chunkNumber = Convert.ToInt32(provider.FormData["flowChunkNumber"]);
                int totalChunks = Convert.ToInt32(provider.FormData["flowTotalChunks"]);
                string identifier = provider.FormData["flowIdentifier"];
                string filename = provider.FormData["flowFilename"];
                // Rename generated file
                MultipartFileData chunk = provider.FileData[0]; // Only one file in multipart message
                RenameChunk(chunk, chunkNumber, identifier);
                // Assemble chunks into single file if they're all here
                TryAssembleFile(identifier, totalChunks, filename);
                // Success
                return Request.CreateResponse(HttpStatusCode.OK);
            }
            catch (System.Exception e)
            {
                return Request.CreateErrorResponse(HttpStatusCode.InternalServerError, e);
            }

        }

        private string GetChunkFileName(int chunkNumber, string identifier)
        {
            return Path.Combine(root, string.Format("{0}_{1}", identifier, chunkNumber.ToString()));
        }

        private void RenameChunk(MultipartFileData chunk, int chunkNumber, string identifier)
        {
            string generatedFileName = chunk.LocalFileName;
            string chunkFileName = GetChunkFileName(chunkNumber, identifier);
            if (File.Exists(chunkFileName)) File.Delete(chunkFileName);
            File.Move(generatedFileName, chunkFileName);

        }

        private string GetFileName(string identifier)
        {
            return Path.Combine(root, identifier);
        }

        private bool ChunkIsHere(int chunkNumber, string identifier)
        {
            string fileName = GetChunkFileName(chunkNumber, identifier);
            return File.Exists(fileName);
        }

        private bool AllChunksAreHere(string identifier, int totalChunks)
        {
            for (int chunkNumber = 1; chunkNumber <= totalChunks; chunkNumber++)
                if (!ChunkIsHere(chunkNumber, identifier)) return false;
            return true;
        }

        private void TryAssembleFile(string identifier, int totalChunks, string filename)
        {
            if (AllChunksAreHere(identifier, totalChunks))
            {
                // Create a single file
                var consolidatedFileName = GetFileName(identifier);
                using (var destStream = File.Create(consolidatedFileName, 15000))
                {
                    for (int chunkNumber = 1; chunkNumber <= totalChunks; chunkNumber++)
                    {
                        var chunkFileName = GetChunkFileName(chunkNumber, identifier);
                        using (var sourceStream = File.OpenRead(chunkFileName))
                        {
                            sourceStream.CopyTo(destStream);
                        }
                    }
                    destStream.Close();
                }
                // Rename consolidated with original name of upload
                filename = Path.GetFileName(filename); // Strip to filename if directory is specified (avoid cross-directory attack)
                string realFileName = Path.Combine(root, filename);
                if (File.Exists(filename)) File.Delete(realFileName);
                File.Move(consolidatedFileName, realFileName);
                // Delete chunk files
                for (int chunkNumber = 1; chunkNumber <= totalChunks; chunkNumber++)
                {
                    var chunkFileName = GetChunkFileName(chunkNumber, identifier);
                    File.Delete(chunkFileName);
                }
            }
        }

    }
}
like image 165
Herb Caudill Avatar answered Nov 20 '22 15:11

Herb Caudill


This repository on Github has implementation of parsing and consolidating of flow js chunks on MVC.

It's very easy to implement. Just drop provided files in your project and your controller will look something like this

-- Disclaimer. I'm the author of this repo.

like image 20
Dmitry Efimenko Avatar answered Nov 20 '22 15:11

Dmitry Efimenko