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
{
var uploadToken = queryString.Get("upload_Token");
int resumableChunkNumber = int.Parse(queryString.Get("resumableChunkNumber"));
var resumableFilename = queryString.Get("resumableFilename");
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);
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
{
var uploadToken = queryString.Get("upload_Token");
int resumableChunkNumber = int.Parse(queryString.Get("resumableChunkNumber"));
var resumableFilename = queryString.Get("resumableFilename");
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;
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);
}
}
}
}
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.