Skip to content

Instantly share code, notes, and snippets.

@duncansmart
Created September 18, 2016 20:57
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save duncansmart/523a893f8c6c07c8bd4eeeeb736ac7e8 to your computer and use it in GitHub Desktop.
Save duncansmart/523a893f8c6c07c8bd4eeeeb736ac7e8 to your computer and use it in GitHub Desktop.
using System;
using System.Diagnostics;
using System.IO;
using System.Linq;
using System.Text;
using System.Web;
using FakeItEasy;
using MimeKit;
using System.Collections.Specialized;
using System.Collections.Generic;
public class FileUploadHelper
{
const string _sampleContentType = "multipart/form-data; boundary=----WebKitFormBoundaryllf4hN4RsKBbOCoZ";
const string _sample = @"------WebKitFormBoundaryllf4hN4RsKBbOCoZ
Content-Disposition: form-data; name=""foo""
bar1
------WebKitFormBoundaryllf4hN4RsKBbOCoZ
Content-Disposition: form-data; name=""foo""
bar2
------WebKitFormBoundaryllf4hN4RsKBbOCoZ
Content-Disposition: form-data; name=""baz""
😂
------WebKitFormBoundaryllf4hN4RsKBbOCoZ
Content-Disposition: form-data; name=""file1""; filename=""hello world.xml""
Content-Type: text/xml
<?xml version=""1.0"" encoding=""utf-8""?>
<hello>world</hello>
------WebKitFormBoundaryllf4hN4RsKBbOCoZ
Content-Disposition: form-data; name=""file2""; filename=""hello world.zip""
Content-Type: application/x-zip-compressed
binary here ...
------WebKitFormBoundaryllf4hN4RsKBbOCoZ--
";
static void ParseMultipartFormData_TEST()
{
var request = A.Fake<HttpRequestBase>();
A.CallTo(() => request.GetBufferlessInputStream()).Returns(new MemoryStream(Encoding.UTF8.GetBytes(_sample)));
A.CallTo(() => request.ContentType).Returns(_sampleContentType);
var mime = ParseMultipartFormData(request);
foreach (var item in mime.OfType<MimePart>())
{
if (item.FileName != null)
{
var saveStream = new MemoryStream();
item.WriteTo(saveStream, contentOnly: true);
var content = Encoding.UTF8.GetString(saveStream.ToArray());
Debug.WriteLine(content);
}
}
}
// https://github.com/jstedfast/MimeKit/blob/master/FAQ.md#SaveAttachments
static Multipart ParseMultipartFormData(HttpRequestBase request)
{
// create a temporary, DeleteOnClose file to store our large HTTP data stream
// will be deleted when Multipart's GC kicks in or when process closes
var stream = new FileStream(
Path.Combine(Path.GetTempPath(), "~formdata-" + Path.GetRandomFileName()),
FileMode.Create,
FileAccess.ReadWrite,
FileShare.Read | FileShare.Delete,
4096,
FileOptions.DeleteOnClose);
// check that upload is multipart/form-data
var requestContentType = ContentType.Parse(request.ContentType);
if (!requestContentType.MimeType.Equals("multipart/form-data", StringComparison.OrdinalIgnoreCase))
throw new ArgumentException($"Expected Content-Type: multipart/form-data, but was '{requestContentType.MimeType}'");
// create a header for the multipart/form-data MIME entity based on the Content-Type value of the HTTP
// response
var header = Encoding.UTF8.GetBytes(string.Format("Content-Type: {0}\r\n\r\n", request.ContentType));
// write the header to the stream
stream.Write(header, 0, header.Length);
// copy the content of the HTTP response to our temporary stream
request.GetBufferlessInputStream().CopyTo(stream);
// reset the stream back to the beginning
stream.Position = 0;
// parse the MIME entity with persistent = true, telling the parser not to load the content into memory
return (Multipart)MimeEntity.Load(stream, persistent: true);
}
static void ParseLargeFileUpload_TEST()
{
var request = A.Fake<HttpRequestBase>();
A.CallTo(() => request.GetBufferlessInputStream()).Returns(new MemoryStream(Encoding.UTF8.GetBytes(_sample)));
A.CallTo(() => request.ContentType).Returns(_sampleContentType);
NameValueCollection form;
List<KeyValuePair<string, HttpPostedFileBase>> files;
ParseLargeFileUpload(request, out form, out files);
Debug.Assert(form.Count == 2);
Debug.Assert(files.Count == 2);
}
static void ParseLargeFileUpload(HttpRequestBase request, out NameValueCollection form, out List<KeyValuePair<string, HttpPostedFileBase>> files)
{
var multipart = ParseMultipartFormData(request);
form = new NameValueCollection();
files = new List<KeyValuePair<string, HttpPostedFileBase>>();
foreach (var part in multipart.OfType<MimePart>())
{
var name = part.ContentDisposition.Parameters["name"];
if (part.FileName != null)
{
files.Add(new KeyValuePair<string, HttpPostedFileBase>(name, new LargePostedFile(part)));
}
else
{
var textpart = part as TextPart;
if (textpart != null)
form.Add(name, textpart.Text);
}
}
}
class LargePostedFile : HttpPostedFileBase
{
MimePart _mimePart;
internal LargePostedFile(MimePart mimePart)
{
_mimePart = mimePart;
}
public override int ContentLength => (int)_mimePart.ContentObject.Stream.Length;
public override string ContentType => _mimePart.ContentType.MimeType;
public override string FileName => _mimePart.FileName;
public override Stream InputStream => _mimePart.ContentObject.Stream;
public override void SaveAs(string filename)
{
_mimePart.WriteTo(filename);
}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment