Click here to Skip to main content
65,938 articles
CodeProject is changing. Read more.
Articles / web / ASP.NET

ResumableJS with ASP.NET MVC

4.20/5 (4 votes)
17 Dec 2012CPOL 21K   649  
Asp.net implementation for the resumable.js

Introduction

I searched a lot for a resumable, chunks based uploader, and finally I found this one: https://github.com/23/resumable.js but the problem was that there is no source code available in ASP.NET. So I decided to implement my own code in order to use it. 

Background 

This uploader method is very safe and fast, because it uses a chunks method (needs html5 in javascript's side), and it upload chunks in parallel.

Using the code

The uploader uses three main Actions (get, post, options):  

The Get method will check if the chunk exists, and you can disable it in javascript by setting the option "Test" to false. 

C#
[HttpGet]
public void UploadFile()
{
    var queryString = Request.QueryString;
    if (queryString.Count == 0) return;

    try
    {
        // Read parameters
        var uploadToken = queryString.Get("upload_Token");
        int resumableChunkNumber = int.Parse(queryString.Get("resumableChunkNumber"));
        //var resumableIdentifier = queryString.Get("resumableIdentifier");
        //var resumableChunkSize = queryString.Get("resumableChunkSize");
        //var resumableTotalSize = queryString.Get("resumableTotalSize");
        var resumableFilename = queryString.Get("resumableFilename");
    

        // Check for existance
        var filePath = string.Format("{0}/{1}/{1}.part{2}", TEMP_URL, 
             uploadToken, resumableChunkNumber.ToString("0000"));
        var localFilePath = Server.MapPath(filePath);
        bool fileExists = System.IO.File.Exists(localFilePath);

        // Create Response
        if (fileExists)
        {
            Response.Status = "200 OK";
            Response.StatusCode = 200;
        }
        else
        {
            Response.Status = "404 Not Found";
            Response.StatusCode = 404;
        }
    }
    catch (Exception exception)
    {
        Logger.Error(this, "Failed to process uploading Get request.", exception);
    }
}  

The options method is used in some browsers (Chrome, Firefox):

C#
[HttpOptions]
[ActionName("UploadFile")]
public void UploadFileOptions()
{
    Response.StatusCode = (int)HttpStatusCode.OK;
}

The post method will save the chunks on the disk: 

C#
[HttpPost]
[ActionName("UploadFile")]
public async void UploadFilePost()
{
    var queryString = Request.Form;
    if (queryString.Count == 0) return;

    try
    {
        // Read parameters
        var uploadToken = queryString.Get("upload_Token");
        int resumableChunkNumber = int.Parse(queryString.Get("resumableChunkNumber"));
        var resumableFilename = queryString.Get("resumableFilename");
       
        // Save File
        if (Request.Files.Count == 0)
        {
            Response.StatusCode = (int)System.Net.HttpStatusCode.InternalServerError;
            Logger.Warn("", "Request sent whithout any files !!");
        }
        else
        {
            var filePath = string.Format("{0}/{1}/{1}.part{2}", 
                 TEMP_URL, uploadToken, resumableChunkNumber.ToString("0000"));
            var localFilePath = Server.MapPath(filePath);
            
            if (!System.IO.File.Exists(localFilePath))
            {
                Request.Files[0].SaveAs(localFilePath);
            }

        }
    }
    catch (Exception exception)
    {
        return null;
    }
} 

Finally the Commit Action, which will merge the chunks into one file: 

C#
[HttpPost]
public JsonResult CommitUpload()
{
    var results = new FileUploadResult();

    try
    {
        var queryString = Request.Form;
        if (queryString.Count == 0) return null;

        // Read parameters
        var uploadToken = queryString.Get("upload_Token");
        var resumableFilename = queryString.Get("resumableFilename");
        int resumableChunkSize = int.Parse(queryString.Get("resumableChunkSize"));
        long resumableTotalSize = long.Parse(queryString.Get("resumableTotalSize"));
       
        string fileUrl = String.Empty;

        var filePath = string.Format("{0}/{1}/", TEMP_URL, uploadToken);
        var directory = WebDirectory.MapPath(filePath);

        lock (_lock)
        {
            if (Directory.Exists(directory))
            {
                var files = Directory.GetFiles(directory);
                if (GetTotalSize(files) == resumableTotalSize)
                {
                    var fileRelativePath = this.SaveUploadedFile(files, resumableFilename);
                    if (!string.IsNullOrEmpty(fileRelativePath))
                    {
                        fileUrl = fileRelativePath.Replace("~/", Site.MediaServerUrl);
                        Directory.Delete(directory);
                    }
                }
            }
        }

        // Generate Result
        results = new FileUploadResult(resumableFilename, fileUrl, true, 
          string.Format(Resources.Global.FileUploadSuccess, resumableFilename));

        Response.Charset = System.Text.Encoding.UTF8.WebName;
        Response.ContentType = "text/html;charset=utf-8";

    }
    catch (Exception exception)
    {
        return null;
    }
 
    return Json(results);
}

And this is the Result Model:

C#
[DataContract]
public class FileUploadResult
{
    [DataMember]
    public string FileName { get; set; }
    [DataMember]
    public string FileUrl { get; set; }
    [DataMember]
    public bool Succeed { get; set; }
    [DataMember]
    public string Message { get; set; }

    public FileUploadResult()
    {
    }

    public FileUploadResult(string fileName, string fileUrl, bool succeed, string message)
    {
        FileName = fileName;
        FileUrl = fileUrl;
        Succeed = succeed;
        Message = message;
    }
}  

You will find the JavaScript use method in the original site.  

Notes 

I modified something in the original JavaScript code, so you cannot use the original file with my implementation. 

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)