Skip to content

Instantly share code, notes, and snippets.

@pdcullen
Created July 28, 2014 13:36
Show Gist options
  • Star 2 You must be signed in to star a gist
  • Fork 3 You must be signed in to fork a gist
  • Save pdcullen/d29cfd26f31d4968490e to your computer and use it in GitHub Desktop.
Save pdcullen/d29cfd26f31d4968490e to your computer and use it in GitHub Desktop.
ng-flow asp.net api2 upload class
using System;
using System.Collections.Generic;
using System.Collections.Specialized;
using System.IO;
using System.Linq;
using System.Net;
using System.Net.Http;
using System.Runtime.Serialization;
using System.Threading.Tasks;
using System.Web;
using System.Web.Http;
using System.Web.Http.Cors;
using System.Web.Mvc;
namespace WHOUploadSPA.Controllers
{
[DataContract]
public class FileDesc
{
[DataMember]
public string name { get; set; }
[DataMember]
public string path { get; set; }
[DataMember]
public long size { get; set; }
public FileDesc(string n, string p, long s)
{
name = n;
path = p;
size = s;
}
}
public class CustomMultipartFormDataStreamProvider : MultipartFormDataStreamProvider
{
public readonly string _filename;
public CustomMultipartFormDataStreamProvider(string path, string filename) : base(path)
{
_filename = filename;
}
public override string GetLocalFileName(System.Net.Http.Headers.HttpContentHeaders headers)
{
return _filename;
}
}
[EnableCors("*", "*", "GET, PUT, POST")]
public class UploadController : ApiController
{
private readonly string BASEFOLDERNAME = "uploads";
private class FlowMeta
{
public string flowChunkNumber { get; set; }
public string flowChunkSize { get; set; }
public string flowCurrentChunkSize { get; set; }
public string flowTotalSize { get; set; }
public string flowIdentifier { get; set; }
public string flowFilename { get; set; }
public string flowRelativePath { get; set; }
public string flowTotalChunks { get; set; }
public FlowMeta(Dictionary<string, string> values)
{
flowChunkNumber = values["flowChunkNumber"];
flowChunkSize = values["flowChunkSize"];
flowCurrentChunkSize = values["flowCurrentChunkSize"];
flowTotalSize = values["flowTotalSize"];
flowIdentifier = values["flowIdentifier"];
flowFilename = values["flowFilename"];
flowRelativePath = values["flowRelativePath"];
flowTotalChunks = values["flowTotalChunks"];
}
public FlowMeta(NameValueCollection values)
{
flowChunkNumber = values["flowChunkNumber"];
flowChunkSize = values["flowChunkSize"];
flowCurrentChunkSize = values["flowCurrentChunkSize"];
flowTotalSize = values["flowTotalSize"];
flowIdentifier = values["flowIdentifier"];
flowFilename = values["flowFilename"];
flowRelativePath = values["flowRelativePath"];
flowTotalChunks = values["flowTotalChunks"];
}
}
public HttpResponseMessage Get()
{
var meta = new FlowMeta(Request.GetQueryNameValuePairs().ToDictionary(x => x.Key, x => x.Value));
var PATH = HttpContext.Current.Server.MapPath(string.Format(@"~/{0}/{1}", BASEFOLDERNAME, meta.flowIdentifier));
var filename = string.Format(@"{0}_{1}", meta.flowFilename, meta.flowChunkNumber.PadLeft(4, '0'));
if ("1".Equals(meta.flowTotalChunks))
{
filename = string.Format(@"{0}", meta.flowFilename);
}
if (File.Exists(Path.Combine(PATH, filename)))
{
return Request.CreateResponse(HttpStatusCode.OK);
}
else
{
return Request.CreateResponse(HttpStatusCode.NotFound);
}
}
public Task<IEnumerable<FileDesc>> Post()
{
var meta = new FlowMeta(HttpContext.Current.Request.Form);
var PATH = HttpContext.Current.Server.MapPath(string.Format(@"~/{0}/{1}", BASEFOLDERNAME, meta.flowIdentifier));
var variables = System.Web.HttpContext.Current.Request.ServerVariables;
var physPath = variables["APPL_PHYSICAL_PATH"];
Directory.CreateDirectory(PATH);
var rootUrl = Request.RequestUri.AbsoluteUri.Replace(Request.RequestUri.AbsolutePath, String.Empty);
if (Request.Content.IsMimeMultipartContent())
{
var filename = string.Format(@"{0}_{1}", meta.flowFilename, meta.flowChunkNumber.PadLeft(4, '0'));
if ("1".Equals(meta.flowTotalChunks))
{
filename = string.Format(@"{0}", meta.flowFilename);
}
var streamProvider = new CustomMultipartFormDataStreamProvider(PATH, filename);
var task = Request.Content.ReadAsMultipartAsync(streamProvider).ContinueWith<IEnumerable<FileDesc>>(t =>
{
if (t.IsFaulted || t.IsCanceled)
{
throw new HttpResponseException(HttpStatusCode.InternalServerError);
}
if (!"1".Equals(meta.flowTotalChunks) && meta.flowTotalChunks.Equals(meta.flowChunkNumber))
{
var dest = Path.Combine(PATH, meta.flowFilename);
using (var fileS = new FileStream(dest, FileMode.Create))
{
var files = Directory.EnumerateFiles(PATH)
.Where(s => !s.Equals(dest))
.OrderBy(s => s);
foreach (var file in files)
{
using (var sourceStream = File.OpenRead(file))
sourceStream.CopyTo(fileS);
}
fileS.Flush();
int expectedBytes;
if (int.TryParse(meta.flowTotalSize, out expectedBytes))
{
var info = new FileInfo(dest);
if (info.Length == expectedBytes)
{
foreach (var file in files)
{
File.Delete(file);
}
}
}
}
var fileInfo = streamProvider.FileData.Select(i =>
{
var uri = dest.Replace(physPath, "~/").Replace(@"\", "/");
var info = new FileInfo(dest);
return new FileDesc(info.Name, uri, info.Length / 1024);
});
return fileInfo;
}
else
{
var fileInfo = streamProvider.FileData.Select(i =>
{
var uri = i.LocalFileName.Replace(physPath, "~/").Replace(@"\", "/");
var info = new FileInfo(i.LocalFileName);
return new FileDesc(info.Name, uri, info.Length / 1024);
});
return fileInfo;
}
});
return task;
}
else
{
throw new HttpResponseException(Request.CreateResponse(HttpStatusCode.NotAcceptable, "This request is not formatted correctly or understood"));
}
}
}
}
@wald-tq
Copy link

wald-tq commented Oct 30, 2014

Thanks for posting this.
I did a own Implementation of a Upload Controller.
Do you have issues when processing the last chunk before all other chunks are present on disk?
I try to incorporate your good ideas into my implementation.

@thomedwards
Copy link

There are times when the chunks do not combine. A new file is created, but the chunks (ending _0001, _0002, etc) are still in the directory. Also, the file some times will not open. For example, if the file is a .zip, WinZip errors saying the file is not valid.

I cannot find any rhyme or reason as to why things work properly some times and not others. I realize I have not given you a great deal of information, but that is all I have. Can you give ANY tips as to what may cause the combining and deleting of the chunks to fail?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment