Click here to Skip to main content
Click here to Skip to main content

Tagged as

ResumableJS with ASP.NET MVC

, 17 Dec 2012 CPOL
Rate this:
Please Sign up or sign in to vote.
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. 

[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):

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

The post method will save the chunks on the disk: 

[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: 

[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:

[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)

Share

About the Author

Ahmad Dekmak
Software Developer (Senior) Mentis
Lebanon Lebanon
No Biography provided

Comments and Discussions

 
QuestionDoes this work with multiple files? PinmembermasterJalchr18-Dec-12 0:28 
AnswerRe: Does this work with multiple files? PinmemberAhmad Dekmak18-Dec-12 1:00 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.

| Advertise | Privacy | Terms of Use | Mobile
Web03 | 2.8.141216.1 | Last Updated 17 Dec 2012
Article Copyright 2012 by Ahmad Dekmak
Everything else Copyright © CodeProject, 1999-2014
Layout: fixed | fluid