Large file size (greater than 4MB ) upload using Async js without Azure Blob Storage

In this Post I will explain how to upload big file to server using asp.net Mvc with async js. Before that we will saw the drawback of traditional way upload step by step.
Add following line in Index.cshtml file from View/Home/Index.cshtml file
@{
ViewBag.Title = "Home Page";
}
<div>
@using (Html.BeginForm("UploadBigFileByTraditional ", "Index", FormMethod.Post, new { enctype = "multipart/form-data" }))
{
<input type="file" name="file" />
<input type="submit" value="upload" />
}
</div>

Add “UploadBigFileByTraditional” in HomeController

[HttpPost]
public ActionResult UploadBigFileByTraditional(HttpPostedFileBase file)
{
file.SaveAs(Server.MapPath("~/ExportFile")+file.FileName);
return RedirectToAction("Index");
}
The page look like following when press F5,Then select one big file size >4 MB
Then Click upload Button you will get following error
In ASP.NET there is a limitation of request length and the maximized request length is defined in the web.config file. It’s a number which less than about 4MB. So if we want to upload a really big file, we cannot simply implement in this way.
Now let see How to solve this problem

Now download async js file from https://github.com/caolan/async and add it into script folder.
Add following line into BundleConfig class from App_Start folder
bundles.Add(new ScriptBundle("~/bundles/asyscjs").Include(
Now refer the script file into Layout.cshtml file

<!DOCTYPE html>
<html lang="en">
<head>
........
@Scripts.Render("~/bundles/asyscjs")
</head>
<body>
..........
</body>
</html>
Edit Index.cshtml file from View/Home/

@{
ViewBag.Title = "Home Page";
}
<div>
<input type="file" id="upload_files" name="files[]" /><br />
Block Size:
<input type="number" id="block_size" value="512" name="block_size" />KB<br />
Please enter Maximum 3500
<input type="button" id="upload_button_blob" name="upload" value="upload (blob)" />
</div>
First check the client browser is support upload big file

<script type="text/javascript">
$(function () {
$("#upload_button_blob").click(function () {
// Check Browser compatibility
if (window.File && window.Blob && window.FormData) {
alert("Your brwoser is awesome, let's rock!");
}
else {
alert("Oh man plz update to a modern browser before try is cool stuff out.");
return;
}
// END Check Browser compatibility
});
</script>

Then handle if user select multiple file
<script type="text/javascript">
$(function () {
$("#upload_button_blob").click(function () {
// Check Browser compatibility
.................
// END Check Browser compatibility
// START Handle If user Select Multiple file
var files = $("#upload_files")[0].files;
for (var i = 0; i < files.length; i++) {
var file = files[i];
var fileSize = file.size;
var fileName = file.name;
}
// END Handle If user Select Multiple file
});
</script>

Then split the big file into small small file based on value enter block_size textbox.For that we need to calculate the start and end byte index for each small file.


<script type="text/javascript">
$(function () {
$("#upload_button_blob").click(function () {
// Check Browser compatibility
...................
// END Check Browser compatibility
// START Handle If user Select Multiple file
var files = $("#upload_files")[0].files;
for (var i = 0; i < files.length; i++) {
..................
// Start calculate the start and end byte index for each blocks(chunks)
// with the index, file name and index list for future using
var blockSizeInKB = $("#block_size").val();
var blockSize = blockSizeInKB * 1024;
var blocks = [];
var offset = 0;
var index = 0;
var list = "";
while (offset < fileSize) {
var start = offset;
var end = Math.min(offset + blockSize, fileSize);
blocks.push({
name: fileName,
index: index,
start: start,
end: end
});
list += index + ",";
offset = end;
index++;
}
// End calculate the start and end byte index for each blocks(chunks)
}
});
});
</script>

Then split the big file based on blocks start and end byte and send it into server. From server we need to save it into tem folder

<script type="text/javascript">

$(function () {

$("#upload_button_blob").click(function () {
// Check Browser compatibility
if (window.File && window.Blob && window.FormData) {
.....................
// END Check Browser compatibility
// START Handle If user Select Multiple file
var files = $("#upload_files")[0].files;
for (var i = 0; i < files.length; i++) {
.............
// Start calculate the start and end byte index for each blocks(chunks)
// with the index, file name and index list for future using
................................
// End calculate the start and end byte index for each blocks(chunks)
var putBlocks = [];
blocks.forEach(function (block) {
putBlocks.push(function (callback) {
// load blob based on the start and end index for each chunks
var blob = file.slice(block.start, block.end);
// put the file name, index and blob into a temporary from
var fd = new FormData();
fd.append("name", block.name);
fd.append("index", block.index);
fd.append("file", blob);
// post the form to backend service (asp.net mvc controller action)
$.ajax({
url: '@Url.Action("UploadBigFile", "Home")',
data: fd,
processData: false,
contentType: false,
type: "POST",
success: function (result) {
if (!result.success) {
alert(result.error);
}
callback(null, block.index);
}
});
});
});
}
});
});
</script>

And add UploadBigFile methods in Home controller page

[HttpPost]
public ActionResult UploadBigFile()
{
var error = string.Empty;
try
{
var name = Request.Form["name"];
var index = int.Parse(Request.Form["index"]);
string filename = index + "." + name.Split('.')[1];
var file = Request.Files[0];
file.SaveAs(Server.MapPath("/ExportTempFile/" + filename));
}
catch (Exception e)
{
error = e.ToString();
}
return new JsonResult(){Data=new{success=string.IsNullOrWhiteSpace(error),error=error}};
}

Now we split single file and save it server tem folder.Finally we need to get all the spited small file from then folder and merge it into single file. for that we need to do following.


<script type="text/javascript">
$(function () {
$("#upload_button_blob").click(function () {
// Check Browser compatibility
if (window.File && window.Blob && window.FormData) {
.....................
// END Check Browser compatibility
// START Handle If user Select Multiple file
var files = $("#upload_files")[0].files;
for (var i = 0; i < files.length; i++) {
.............
// Start calculate the start and end byte index for each blocks(chunks)
// with the index, file name and index list for future using
................................
// End calculate the start and end byte index for each blocks(chunks)
var putBlocks = [];
blocks.forEach(function (block) { ..........................................
});
// then invoke the commit ajax call to put blocks into original folder
async.series(putBlocks, function (error, result) {
var data = {
name: fileName,
list: list
};
$.post('@Url.Action("Commit", "Home")', data, function (result) {
});
}).done(function (e) {
$.post('@Url.Action("DeleteTem", "Home")', data, function (result) {
});
});

}
});
});
</script>

[HttpPost]
public JsonResult Commit()
{
var error = string.Empty;
try
{
string path = Server.MapPath("/ExportTempFile");
if (Directory.Exists(path))
{
var directoryInfo = new DirectoryInfo(path);
var files = from file in directoryInfo.EnumerateFiles()
where file.Name.Split('.')[1] == "xlsx"
select new KeyValuePair<int,string>
(int.Parse(file.Name.Split('.')[0]),
file.FullName);

var fileAry = files.OrderBy(a => a.Key).Select(a => a.Value).ToArray();

using (var destStream = System.IO.File.Create(Server.MapPath("/ExportFile/new.xlsx")))
{
foreach (string filePath in fileAry)
{
using (var sourceStream = System.IO.File.OpenRead(filePath))
{
sourceStream.CopyTo(destStream); // You can pass the buffer size as second argument.
}
}
}
}
}
catch (Exception e)
{
error = e.ToString();
}
return new JsonResult() { Data = new { success = string.IsNullOrWhiteSpace(error), error = error } };
}


public JsonResult DeleteTem()
{ string path = Server.MapPath("/ExportTempFile");
Directory.Delete(path);
return Json("SUCCESS", JsonRequestBehavior.AllowGet);
}

Thanks.

No comments:

Post a Comment