This is continuation of blog posts part 1 and part 2
this post covers possible alternatives on implementing large file upload in asp.net
using fileupload control
- SaveAs- internal implementation is explained part 2
- property FileContent or PostedFile.InputStream. This again is using the HttpRawUploadedContent +temp file approach,but you can you get the content as a stream object,which you can use to save to a DB or any other location.
Note: never use the property FileBytes,which loads all the file contents to memory.
- HttpWorkerRequest and HttpMultipartContentTemplateParser
One of is the most powerful class in asp.net is HttpWorkerRequeust .Because this where the whole asp.net framework implementation starts.For a long time i thought the asp.net framework most powerful things are HttpModules and handlers but i was wrong
From IIS,if you want anything to do in the managed asp.netframework,it starts with an instance of HttpWorkerRequest
so for file upload we will have to rely on few methods of HttpWorkerRequest like GetTotalEntityBodyLength(), GetPreloadedEntityBody() and ReadEntityBody().
Lets use the httpworker request class
a basic implementation looks like this
HttpContext downloadContext = HttpContext.Current;
FileStream fStream = null;
// Check if body contains data
if (_workerRequest.HasEntityBody())
{
// get the total body length
int requestLength = _workerRequest.GetTotalEntityBodyLength();
// Get the initial bytes loaded
//wr.GetPreloadedEntityBody() ?? wr.GetPreloadedEntityBody().Length;
byte[] _data=new byte[requestLength];
if (!_workerRequest.IsEntireEntityBodyIsPreloaded())
{
byte[] buffer = new byte[512000];
string[] fileName = downloadContext.Request.QueryString["fileName"].Split(new char[] { '\\' });
DirectoryInfo dir = Directory.CreateDirectory(downloadContext.Server.MapPath("~/Uploads") + "/" + Guid.NewGuid().ToString());
fStream = new FileStream(Path.Combine(dir.FullName, fileName[fileName.Length - 1]), FileMode.CreateNew);
// Set the received bytes to initial bytes before start reading
int receivedBytes, bytesToRead;
receivedBytes = _workerRequest.GetPreloadedEntityBodyLength();
if (receivedBytes > 0)
{
fStream.Write(_workerRequest.GetPreloadedEntityBody(), 0, receivedBytes);
}
while (requestLength > receivedBytes)
{
bytesToRead = requestLength - receivedBytes;
int bytesRead;
if (bytesToRead <= buffer.Length)
{
bytesRead = _workerRequest.ReadEntityBody(buffer, bytesToRead);
}
else
{
bytesRead = _workerRequest.ReadEntityBody(buffer, buffer.Length);
}
// Write the chunks to the physical file
fStream.Write(buffer, 0, bytesRead);
// Update the received bytes
receivedBytes += bytesRead;
}
fStream.Seek(0, SeekOrigin.Begin);
fStream.Read(_data, 0, requestLength);
//initialBytes = workerRequest.ReadEntityBody(buffer, requestLength - receivedBytes);
fStream.Flush();
fStream.Close();
return _data;
}
}
Now Lets have a revision of HTTP upload process.We have to have a <INPUT type=”file”> and/or ENCTYPE=”multipart/form-data” specified in the form tag.
http://en.wikipedia.org/wiki/File_select
http://www.faqs.org/rfcs/rfc1867.html
When the content gets to server,it will look like this( i tried uploading an image to and this is what got uploaded to site in question.Seeing the http traffic using fiddler http://www.fiddler2.com/fiddler2/ )
POST http://<hostname>/convert/pic2html.cgi HTTP/1.1
Accept: text/html, application/xhtml+xml, */*
Referer: http://<hostname>/convert/
Accept-Language: en-IN
User-Agent: Mozilla/5.0 (compatible; MSIE 9.0; Windows NT 6.1; Trident/5.0)
Content-Type: multipart/form-data; boundary=—————————7dc10b1420476
Accept-Encoding: gzip, deflate
Host: www.text-image.com
Content-Length: 11182
Connection: Keep-Alive
Pragma: no-cache
—————————–7dc10b1420476
Content-Disposition: form-data; name=”image”; filename=”actionmethods.png”
Content-Type: image/x-png
�PNG
<<Content Goes here…Trimmed for brevity>>
<if you have other form input fields,we will see those as well>>
Content-Disposition: form-data; name=”characters”
01
—————————–7dc10b1420476
Content-Disposition: form-data; name=”textType”
random
—————————–7dc10b1420476
Content-Disposition: form-data; name=”fontsize”
-3
—————————–7dc10b1420476
Content-Disposition: form-data; name=”width”
130
—————————–7dc10b1420476
Content-Disposition: form-data; name=”grayscale”
0
—————————–7dc10b1420476
Content-Disposition: form-data; name=”bgcolor”
BLACK
—————————–7dc10b1420476
Content-Disposition: form-data; name=”contrast”
0
—————————–7dc10b1420476
Content-Disposition: form-data; name=”browser”
firefox
—————————–7dc10b1420476–
Why this is important ? Because if you are going to implement your own upload ,you will have to implement a parser which can parse this multi-part form data .By default asp.net does this for you (implemented with the help of following classes).
HttpMultipartContentTemplateParser
HttpRawUploadedContent
MultipartContentElement
If you would like to see a sample implementation,please check https://bitbucket.org/lorenzopolidori/http-form-parser/src
You may find many more custom implementation in codeplex. So if you would like to implement a custom upload,you have to implement a multi part parser which can read the file in chunked form(from multi part data) and then process it .I would like to add a sample implementation later to this post but it is not yet ready.This is the roadmap of things i would like to implement
- implement a custom multi part parser- No need to reinvent the wheel here,can snatch code from .net framework implementation
http://referencesource.microsoft.com/netframework.aspx .or we can always deassemble System.Web.dll using Reflector ILSPY http://wiki.sharpdevelop.net/ILSpy.ashx
- use HTML5 File Blob api to chunk the upload from client without relying on the browser to do (this is for control freeks to get complete control of resumable/pausable/abortable file upload) https://github.com/blueimp/jQuery-File-Upload/wiki/Chunked-file-uploads
- Implement websocket support(yes use IIS 8) for the same