Skip to content

Commit 7252176

Browse files
author
Sergiy Dobychyn
committed
Async handler added
1 parent e7f7b31 commit 7252176

File tree

1 file changed

+319
-0
lines changed

1 file changed

+319
-0
lines changed

server/asp_net/Handler.ashx

Lines changed: 319 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,319 @@
1+
<%@ WebHandler Language="C#" Class="Handler" %>
2+
3+
using System;
4+
using System.Web;
5+
using System.Collections.Generic;
6+
using System.IO;
7+
using System.Runtime.Serialization;
8+
9+
// app.js
10+
/*
11+
var isOnGitHub = window.location.hostname === 'blueimp.github.io',
12+
url = 'server/asp_net/Handler.ashx';
13+
*/
14+
15+
//main.js
16+
/*
17+
// Initialize the jQuery File Upload widget:
18+
$('#fileupload').fileupload({
19+
// Uncomment the following to send cross-domain cookies:
20+
//xhrFields: {withCredentials: true},
21+
url: 'server/asp_net/Handler.ashx'
22+
});
23+
*/
24+
25+
public class Handler : IHttpAsyncHandler
26+
{
27+
private static readonly FilesDisposition FILES_DISPOSITION = FilesDisposition.Absolute;
28+
private static readonly string FILES_PATH = @"c:\temp\uploader";
29+
30+
private static readonly string FILE_QUERY_VAR = "file";
31+
private static readonly string FILE_GET_CONTENT_TYPE = "application/octet-stream";
32+
33+
private static readonly int ATTEMPTS_TO_WRITE = 3;
34+
private static readonly int ATTEMPT_WAIT = 100; //msec
35+
36+
private static readonly int BUFFER_SIZE = 4 * 1024 * 1024;
37+
38+
private enum FilesDisposition
39+
{
40+
ServerRoot,
41+
HandlerRoot,
42+
Absolute
43+
}
44+
45+
private static class HttpMethods
46+
{
47+
public static readonly string GET = "GET";
48+
public static readonly string POST = "POST";
49+
public static readonly string DELETE = "DELETE";
50+
}
51+
52+
[DataContract]
53+
private class FileResponse
54+
{
55+
[DataMember]
56+
public string name;
57+
[DataMember]
58+
public long size;
59+
[DataMember]
60+
public string type;
61+
[DataMember]
62+
public string url;
63+
[DataMember]
64+
public string error;
65+
[DataMember]
66+
public string deleteUrl;
67+
[DataMember]
68+
public string deleteType;
69+
}
70+
71+
[DataContract]
72+
private class UploaderResponse
73+
{
74+
[DataMember]
75+
public FileResponse[] files;
76+
77+
public UploaderResponse(FileResponse[] fileResponses)
78+
{
79+
files = fileResponses;
80+
}
81+
}
82+
83+
private static string CreateFileUrl(HttpRequest request, string fileName, FilesDisposition filesDisposition)
84+
{
85+
switch (filesDisposition)
86+
{
87+
case FilesDisposition.ServerRoot:
88+
// 1. files directory lies in root directory catalog WRONG
89+
return String.Format("{0}{1}/{2}", request.Url.GetLeftPart(UriPartial.Authority),
90+
FILES_PATH, Path.GetFileName(fileName));
91+
92+
case FilesDisposition.HandlerRoot:
93+
// 2. files directory lays in current page catalog WRONG
94+
return String.Format("{0}{1}{2}/{3}", request.Url.GetLeftPart(UriPartial.Authority),
95+
Path.GetDirectoryName(request.CurrentExecutionFilePath).Replace(@"\", @"/"), FILES_PATH, Path.GetFileName(fileName));
96+
97+
case FilesDisposition.Absolute:
98+
// 3. files directory lays anywhere YEAH
99+
return String.Format("{0}?{1}={2}", request.Url.AbsoluteUri, FILE_QUERY_VAR, HttpUtility.UrlEncode(Path.GetFileName(fileName)));
100+
default:
101+
return String.Empty;
102+
}
103+
}
104+
105+
private static FileResponse CreateFileResponse(HttpRequest request, string fileName, long size, string error)
106+
{
107+
return new FileResponse()
108+
{
109+
name = Path.GetFileName(fileName),
110+
size = size,
111+
type = String.Empty,
112+
url = CreateFileUrl(request, fileName, FILES_DISPOSITION),
113+
error = error,
114+
deleteUrl = CreateFileUrl(request, fileName, FilesDisposition.Absolute),
115+
deleteType = HttpMethods.DELETE
116+
};
117+
}
118+
119+
private static void SerializeUploaderResponse(HttpResponse response, List<FileResponse> fileResponses)
120+
{
121+
122+
var Serializer = new global::System.Runtime.Serialization.Json.DataContractJsonSerializer(typeof(UploaderResponse));
123+
124+
Serializer.WriteObject(response.OutputStream, new UploaderResponse(fileResponses.ToArray()));
125+
}
126+
127+
private static void FromStreamToStream(Stream source, Stream destination)
128+
{
129+
int BufferSize = source.Length >= BUFFER_SIZE ? BUFFER_SIZE : (int)source.Length;
130+
long BytesLeft = source.Length;
131+
132+
byte[] Buffer = new byte[BufferSize];
133+
134+
int BytesRead = 0;
135+
136+
while (BytesLeft > 0)
137+
{
138+
BytesRead = source.Read(Buffer, 0, BytesLeft > BufferSize ? BufferSize : (int)BytesLeft);
139+
140+
destination.Write(Buffer, 0, BytesRead);
141+
142+
BytesLeft -= BytesRead;
143+
}
144+
}
145+
146+
#region IHttpAsyncHandler
147+
148+
private ProcessRequestDelegate RequestDelegate;
149+
private delegate void ProcessRequestDelegate(HttpContext context);
150+
151+
public IAsyncResult BeginProcessRequest(HttpContext context, AsyncCallback cb, object extraData)
152+
{
153+
RequestDelegate = new ProcessRequestDelegate(ProcessRequest);
154+
155+
return RequestDelegate.BeginInvoke(context, cb, extraData);
156+
}
157+
158+
public void EndProcessRequest(IAsyncResult result)
159+
{
160+
RequestDelegate.EndInvoke(result);
161+
}
162+
163+
public bool IsReusable
164+
{
165+
get { return false; }
166+
}
167+
168+
public void ProcessRequest(HttpContext context)
169+
{
170+
string FilesPath;
171+
172+
switch (FILES_DISPOSITION)
173+
{
174+
case FilesDisposition.ServerRoot:
175+
FilesPath = context.Server.MapPath(FILES_PATH);
176+
break;
177+
case FilesDisposition.HandlerRoot:
178+
FilesPath = context.Server.MapPath(Path.GetDirectoryName(context.Request.CurrentExecutionFilePath) + FILES_PATH);
179+
break;
180+
case FilesDisposition.Absolute:
181+
FilesPath = FILES_PATH;
182+
break;
183+
default:
184+
context.Response.StatusCode = 500;
185+
context.Response.StatusDescription = "Configuration error (FILES_DISPOSITION)";
186+
return;
187+
}
188+
189+
// prepare directory
190+
if (!Directory.Exists(FilesPath))
191+
{
192+
Directory.CreateDirectory(FilesPath);
193+
}
194+
195+
196+
string QueryFileName = context.Request[FILE_QUERY_VAR];
197+
string FullFileName = null;
198+
string ShortFileName = null;
199+
200+
//if (!String.IsNullOrEmpty(QueryFileName))
201+
if (QueryFileName != null) // param specified, but maybe in wrong format (empty). else user will download json with listed files
202+
{
203+
ShortFileName = HttpUtility.UrlDecode(QueryFileName);
204+
FullFileName = String.Format(@"{0}\{1}", FilesPath, ShortFileName);
205+
206+
if (QueryFileName.Trim().Length == 0 || !File.Exists(FullFileName))
207+
{
208+
context.Response.StatusCode = 404;
209+
context.Response.StatusDescription = "File not found";
210+
211+
context.Response.End();
212+
return;
213+
}
214+
}
215+
216+
if (context.Request.HttpMethod.ToUpper() == HttpMethods.GET)
217+
{
218+
if (FullFileName != null)
219+
{
220+
context.Response.ContentType = FILE_GET_CONTENT_TYPE; // http://www.digiblog.de/2011/04/android-and-the-download-file-headers/ :)
221+
context.Response.AddHeader("Content-Disposition", String.Format("attachment; filename={0}{1}", Path.GetFileNameWithoutExtension(ShortFileName), Path.GetExtension(ShortFileName).ToUpper()));
222+
223+
using (FileStream FileReader = new FileStream(FullFileName, FileMode.Open, FileAccess.Read))
224+
{
225+
FromStreamToStream(FileReader, context.Response.OutputStream);
226+
227+
context.Response.OutputStream.Close();
228+
}
229+
230+
context.Response.End();
231+
return;
232+
}
233+
else
234+
{
235+
List<FileResponse> FileResponseList = new List<FileResponse>();
236+
237+
string[] FileNames = Directory.GetFiles(FilesPath);
238+
239+
foreach (string FileName in FileNames)
240+
{
241+
FileResponseList.Add(CreateFileResponse(context.Request, FileName, new FileInfo(FileName).Length, String.Empty));
242+
}
243+
244+
SerializeUploaderResponse(context.Response, FileResponseList);
245+
}
246+
}
247+
else if (context.Request.HttpMethod.ToUpper() == HttpMethods.POST)
248+
{
249+
List<FileResponse> FileResponseList = new List<FileResponse>();
250+
251+
for (int FileIndex = 0; FileIndex < context.Request.Files.Count; FileIndex++)
252+
{
253+
HttpPostedFile File = context.Request.Files[FileIndex];
254+
255+
string FileName = String.Format(@"{0}\{1}", FilesPath, Path.GetFileName(File.FileName));
256+
string ErrorMessage = String.Empty;
257+
258+
for (int Attempts = 0; Attempts < ATTEMPTS_TO_WRITE; Attempts++)
259+
{
260+
ErrorMessage = String.Empty;
261+
262+
if (System.IO.File.Exists(FileName))
263+
{
264+
FileName = String.Format(@"{0}\{1}_{2:yyyyMMddHHmmss.fff}{3}", FilesPath, Path.GetFileNameWithoutExtension(FileName), DateTime.Now, Path.GetExtension(FileName));
265+
}
266+
267+
try
268+
{
269+
using (Stream FileStreamWriter = new FileStream(FileName, FileMode.CreateNew, FileAccess.Write))
270+
{
271+
FromStreamToStream(context.Request.InputStream, FileStreamWriter);
272+
}
273+
}
274+
catch (Exception exception)
275+
{
276+
ErrorMessage = exception.Message;
277+
System.Threading.Thread.Sleep(ATTEMPT_WAIT);
278+
continue;
279+
}
280+
281+
break;
282+
}
283+
284+
FileResponseList.Add(CreateFileResponse(context.Request, File.FileName, File.ContentLength, ErrorMessage));
285+
}
286+
287+
SerializeUploaderResponse(context.Response, FileResponseList);
288+
}
289+
else if (context.Request.HttpMethod.ToUpper() == HttpMethods.DELETE)
290+
{
291+
bool SuccessfullyDeleted = true;
292+
293+
try
294+
{
295+
File.Delete(FullFileName);
296+
}
297+
catch
298+
{
299+
SuccessfullyDeleted = false;
300+
}
301+
302+
context.Response.Write(String.Format("{{\"{0}\":{1}}}", ShortFileName, SuccessfullyDeleted.ToString().ToLower()));
303+
}
304+
else
305+
{
306+
context.Response.StatusCode = 405;
307+
context.Response.StatusDescription = "Method not allowed";
308+
context.Response.End();
309+
310+
return;
311+
}
312+
313+
314+
context.Response.End();
315+
}
316+
317+
#endregion
318+
319+
}

0 commit comments

Comments
 (0)