Skip to content

Commit b718028

Browse files
committed
Added large file upload support via http handler/web.config setting
1 parent 1197bf1 commit b718028

File tree

11 files changed

+377
-150
lines changed

11 files changed

+377
-150
lines changed
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
<?xml version="1.0" encoding="utf-8" standalone="yes"?>
2+
<map />

jQuery-File-Upload.MVC3/Content/FileUpload/main.js

+1-2
Original file line numberDiff line numberDiff line change
@@ -19,8 +19,7 @@ $(function () {
1919
$('#fileupload').fileupload();
2020

2121
$('#fileupload').fileupload('option', {
22-
maxFileSize: 5000000,
23-
acceptFileTypes: /(\.|\/)(gif|jpe?g|png)$/i,
22+
maxFileSize: 500000000,
2423
resizeMaxWidth: 1920,
2524
resizeMaxHeight: 1200
2625
});

jQuery-File-Upload.MVC3/Content/Site.css

+6
Original file line numberDiff line numberDiff line change
@@ -302,3 +302,9 @@ div#title {
302302
.tri-state {
303303
width: 6em;
304304
}
305+
306+
.preview img
307+
{
308+
max-width: 80px;
309+
max-height: 80px;
310+
}
Loading

jQuery-File-Upload.MVC3/Controllers/HomeController.cs

+12-2
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@ public ActionResult Index()
1919
return View();
2020
}
2121

22+
//DONT USE THIS IF YOU NEED TO ALLOW LARGE FILES UPLOADS
2223
[HttpGet]
2324
public void Delete(string id)
2425
{
@@ -31,6 +32,7 @@ public void Delete(string id)
3132
}
3233
}
3334

35+
//DONT USE THIS IF YOU NEED TO ALLOW LARGE FILES UPLOADS
3436
[HttpGet]
3537
public void Download(string id)
3638
{
@@ -50,6 +52,7 @@ public void Download(string id)
5052
context.Response.StatusCode = 404;
5153
}
5254

55+
//DONT USE THIS IF YOU NEED TO ALLOW LARGE FILES UPLOADS
5356
[HttpPost]
5457
public ActionResult UploadFiles()
5558
{
@@ -83,12 +86,14 @@ private string EncodeFile(string fileName)
8386
return Convert.ToBase64String(System.IO.File.ReadAllBytes(fileName));
8487
}
8588

89+
//DONT USE THIS IF YOU NEED TO ALLOW LARGE FILES UPLOADS
8690
//Credit to i-e-b and his ASP.Net uploader for the bulk of the upload helper methods - https://github.com/i-e-b/jQueryFileUpload.Net
8791
private void UploadPartialFile(string fileName, HttpRequestBase request, List<ViewDataUploadFilesResult> statuses)
8892
{
8993
if (request.Files.Count != 1) throw new HttpRequestValidationException("Attempt to upload chunked file containing more than one fragment per request");
9094
var file = request.Files[0];
9195
var inputStream = file.InputStream;
96+
9297
var fullName = Path.Combine(StorageRoot, Path.GetFileName(fileName));
9398

9499
using (var fs = new FileStream(fullName, FileMode.Append, FileAccess.Write))
@@ -111,26 +116,31 @@ private void UploadPartialFile(string fileName, HttpRequestBase request, List<Vi
111116
type = file.ContentType,
112117
url = "/Home/Download/" + fileName,
113118
delete_url = "/Home/Delete/" + fileName,
119+
thumbnail_url = @"data:image/png;base64," + EncodeFile(fullName),
114120
delete_type = "GET",
115121
});
116122
}
117123

124+
//DONT USE THIS IF YOU NEED TO ALLOW LARGE FILES UPLOADS
118125
//Credit to i-e-b and his ASP.Net uploader for the bulk of the upload helper methods - https://github.com/i-e-b/jQueryFileUpload.Net
119126
private void UploadWholeFile(HttpRequestBase request, List<ViewDataUploadFilesResult> statuses)
120127
{
121128
for (int i = 0; i < request.Files.Count; i++)
122129
{
123130
var file = request.Files[i];
124-
file.SaveAs(Path.Combine(StorageRoot, Path.GetFileName(file.FileName)));
125131

126-
string fullName = Path.GetFileName(file.FileName);
132+
var fullPath = Path.Combine(StorageRoot, Path.GetFileName(file.FileName));
133+
134+
file.SaveAs(fullPath);
135+
127136
statuses.Add(new ViewDataUploadFilesResult()
128137
{
129138
name = file.FileName,
130139
size = file.ContentLength,
131140
type = file.ContentType,
132141
url = "/Home/Download/" + file.FileName,
133142
delete_url = "/Home/Delete/" + file.FileName,
143+
thumbnail_url = @"data:image/png;base64," + EncodeFile(fullPath),
134144
delete_type = "GET",
135145
});
136146
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,52 @@
1+
using System;
2+
using System.IO;
3+
4+
namespace jQuery_File_Upload.MVC3.Upload
5+
{
6+
public class FilesStatus
7+
{
8+
public const string HandlerPath = "/";
9+
10+
public string group { get; set; }
11+
public string name { get; set; }
12+
public string type { get; set; }
13+
public int size { get; set; }
14+
public string progress { get; set; }
15+
public string url { get; set; }
16+
public string thumbnail_url { get; set; }
17+
public string delete_url { get; set; }
18+
public string delete_type { get; set; }
19+
public string error { get; set; }
20+
21+
public FilesStatus() { }
22+
23+
public FilesStatus(FileInfo fileInfo) { SetValues(fileInfo.Name, (int)fileInfo.Length, fileInfo.FullName); }
24+
25+
public FilesStatus(string fileName, int fileLength, string fullPath) { SetValues(fileName, fileLength, fullPath); }
26+
27+
private void SetValues(string fileName, int fileLength, string fullPath)
28+
{
29+
name = fileName;
30+
type = "image/png";
31+
size = fileLength;
32+
progress = "1.0";
33+
url = HandlerPath + "FileTransferHandler.ashx?f=" + fileName;
34+
delete_url = HandlerPath + "FileTransferHandler.ashx?f=" + fileName;
35+
delete_type = "DELETE";
36+
37+
var fileSize = ConvertBytesToMegabytes(new FileInfo(fullPath).Length);
38+
if (size > 3) thumbnail_url = "/Content/img/generalFile.png";
39+
else thumbnail_url = @"data:image/png;base64," + EncodeFile(fullPath);
40+
}
41+
42+
private string EncodeFile(string fileName)
43+
{
44+
return Convert.ToBase64String(System.IO.File.ReadAllBytes(fileName));
45+
}
46+
47+
static double ConvertBytesToMegabytes(long bytes)
48+
{
49+
return (bytes / 1024f) / 1024f;
50+
}
51+
}
52+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
<%@ WebHandler Language="C#" CodeBehind="UploadHandler.ashx.cs" Class="jQuery_File_Upload.MVC3.Upload.UploadHandler" %>
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,191 @@
1+
using System.Collections.Generic;
2+
using System.IO;
3+
using System.Linq;
4+
using System.Web;
5+
using System.Web.Script.Serialization;
6+
7+
namespace jQuery_File_Upload.MVC3.Upload
8+
{
9+
/// <summary>
10+
/// Summary description for UploadHandler
11+
/// </summary>
12+
public class UploadHandler : IHttpHandler
13+
{
14+
private readonly JavaScriptSerializer js = new JavaScriptSerializer();
15+
16+
private string StorageRoot
17+
{
18+
get { return Path.Combine(System.Web.HttpContext.Current.Server.MapPath("~/Files")); }
19+
}
20+
21+
public bool IsReusable { get { return false; } }
22+
23+
public void ProcessRequest(HttpContext context)
24+
{
25+
context.Response.AddHeader("Pragma", "no-cache");
26+
context.Response.AddHeader("Cache-Control", "private, no-cache");
27+
28+
HandleMethod(context);
29+
}
30+
31+
// Handle request based on method
32+
private void HandleMethod(HttpContext context)
33+
{
34+
switch (context.Request.HttpMethod)
35+
{
36+
case "HEAD":
37+
case "GET":
38+
if (GivenFilename(context)) DeliverFile(context);
39+
else ListCurrentFiles(context);
40+
break;
41+
42+
case "POST":
43+
case "PUT":
44+
UploadFile(context);
45+
break;
46+
47+
case "DELETE":
48+
DeleteFile(context);
49+
break;
50+
51+
case "OPTIONS":
52+
ReturnOptions(context);
53+
break;
54+
55+
default:
56+
context.Response.ClearHeaders();
57+
context.Response.StatusCode = 405;
58+
break;
59+
}
60+
}
61+
62+
private static void ReturnOptions(HttpContext context)
63+
{
64+
context.Response.AddHeader("Allow", "DELETE,GET,HEAD,POST,PUT,OPTIONS");
65+
context.Response.StatusCode = 200;
66+
}
67+
68+
// Delete file from the server
69+
private void DeleteFile(HttpContext context)
70+
{
71+
var filePath = StorageRoot + context.Request["f"];
72+
if (File.Exists(filePath))
73+
{
74+
File.Delete(filePath);
75+
}
76+
}
77+
78+
// Upload file to the server
79+
private void UploadFile(HttpContext context)
80+
{
81+
var statuses = new List<FilesStatus>();
82+
var headers = context.Request.Headers;
83+
84+
if (string.IsNullOrEmpty(headers["X-File-Name"]))
85+
{
86+
UploadWholeFile(context, statuses);
87+
}
88+
else
89+
{
90+
UploadPartialFile(headers["X-File-Name"], context, statuses);
91+
}
92+
93+
WriteJsonIframeSafe(context, statuses);
94+
}
95+
96+
// Upload partial file
97+
private void UploadPartialFile(string fileName, HttpContext context, List<FilesStatus> statuses)
98+
{
99+
if (context.Request.Files.Count != 1) throw new HttpRequestValidationException("Attempt to upload chunked file containing more than one fragment per request");
100+
var inputStream = context.Request.Files[0].InputStream;
101+
var fullName = StorageRoot + "/" + Path.GetFileName(fileName);
102+
103+
using (var fs = new FileStream(fullName, FileMode.Append, FileAccess.Write))
104+
{
105+
var buffer = new byte[1024];
106+
107+
var l = inputStream.Read(buffer, 0, 1024);
108+
while (l > 0)
109+
{
110+
fs.Write(buffer, 0, l);
111+
l = inputStream.Read(buffer, 0, 1024);
112+
}
113+
fs.Flush();
114+
fs.Close();
115+
}
116+
statuses.Add(new FilesStatus(new FileInfo(fullName)));
117+
}
118+
119+
// Upload entire file
120+
private void UploadWholeFile(HttpContext context, List<FilesStatus> statuses)
121+
{
122+
for (int i = 0; i < context.Request.Files.Count; i++)
123+
{
124+
var file = context.Request.Files[i];
125+
126+
var fullPath = StorageRoot + "/" + Path.GetFileName(file.FileName);
127+
128+
file.SaveAs(fullPath);
129+
130+
string fullName = Path.GetFileName(file.FileName);
131+
statuses.Add(new FilesStatus(fullName, file.ContentLength, fullPath));
132+
}
133+
}
134+
135+
private void WriteJsonIframeSafe(HttpContext context, List<FilesStatus> statuses)
136+
{
137+
context.Response.AddHeader("Vary", "Accept");
138+
try
139+
{
140+
if (context.Request["HTTP_ACCEPT"].Contains("application/json"))
141+
context.Response.ContentType = "application/json";
142+
else
143+
context.Response.ContentType = "text/plain";
144+
}
145+
catch
146+
{
147+
context.Response.ContentType = "text/plain";
148+
}
149+
150+
var jsonObj = js.Serialize(statuses.ToArray());
151+
context.Response.Write(jsonObj);
152+
}
153+
154+
private static bool GivenFilename(HttpContext context)
155+
{
156+
return !string.IsNullOrEmpty(context.Request["f"]);
157+
}
158+
159+
private void DeliverFile(HttpContext context)
160+
{
161+
var filename = context.Request["f"];
162+
var filePath = StorageRoot + filename;
163+
164+
if (File.Exists(filePath))
165+
{
166+
context.Response.AddHeader("Content-Disposition", "attachment; filename=\"" + filename + "\"");
167+
context.Response.ContentType = "application/octet-stream";
168+
context.Response.ClearContent();
169+
context.Response.WriteFile(filePath);
170+
}
171+
else
172+
context.Response.StatusCode = 404;
173+
}
174+
175+
private void ListCurrentFiles(HttpContext context)
176+
{
177+
var files =
178+
new DirectoryInfo(StorageRoot)
179+
.GetFiles("*", SearchOption.TopDirectoryOnly)
180+
.Where(f => !f.Attributes.HasFlag(FileAttributes.Hidden))
181+
.Select(f => new FilesStatus(f))
182+
.ToArray();
183+
184+
string jsonObj = js.Serialize(files);
185+
context.Response.AddHeader("Content-Disposition", "inline; filename=\"files.json\"");
186+
context.Response.Write(jsonObj);
187+
context.Response.ContentType = "application/json";
188+
}
189+
190+
}
191+
}

jQuery-File-Upload.MVC3/Views/Home/Index.cshtml

+12-1
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,18 @@
3232
<!-- Shim to make HTML5 elements usable in older Internet Explorer versions -->
3333
<!--[if lt IE 9]><script src="http://html5shim.googlecode.com/svn/trunk/html5.js"></script><![endif]-->
3434

35-
<form id="fileupload" action="@Url.Action("UploadFiles")" method="POST" enctype="multipart/form-data">
35+
36+
@*IN ORDER TO USE MVC ACTIONS AS HANDLERS OF AJAX CALLS, USE THE FORM DECLARATION BELOW. (THE ONE COMMENTED OUT)
37+
IT IS NOT ADVISED SINCE WHEN USING MVC CONTROLLER TO HANDLE REQUESTS ONE CAN'T CONTROL THE maxMessageLength OF THE POST REQUEST
38+
THIS CASTS THE FUNCTIONALITY OF UPLOADING LARGE FILES USELESS, UNLESS YOU SUCRIFICE THE SECURITY AND ALLOW LARGE
39+
POST MESSAGE SIZES SITE-WIDE.
40+
41+
IT IS BETTER TO USE HTTP HANDLER TO PROCESS UPLOAD REQUESTS UNTIL MVC FRAMEWORK PROVIDES WAYS TO SET maxMessageLength ON
42+
PER ACTION BASIS *@
43+
44+
@*<form id="fileupload" action="@Url.Action("UploadFiles")" method="POST" enctype="multipart/form-data">*@
45+
46+
<form id="fileupload" action="/Upload/UploadHandler.ashx" method="POST" enctype="multipart/form-data">
3647
<!-- The fileupload-buttonbar contains buttons to add/delete files and start/cancel the upload -->
3748
<div class="row fileupload-buttonbar">
3849
<div class="span7">

0 commit comments

Comments
 (0)