Skip to content

Instantly share code, notes, and snippets.

@maxfridbe
Created August 26, 2014 15:52
Show Gist options
  • Save maxfridbe/7e95101381c3cd95be26 to your computer and use it in GitHub Desktop.
Save maxfridbe/7e95101381c3cd95be26 to your computer and use it in GitHub Desktop.
File Upload multipart
//API
public async Task<HttpResponseMessage> DeployPackage()
{
if (!Request.Content.IsMimeMultipartContent("form-data"))
{
throw new HttpResponseException(HttpStatusCode.BadRequest);
}
var provider = await Request.Content.ReadAsMultipartAsync(new InMemoryMultipartFormDataStreamProvider());
//access form data
NameValueCollection formData = provider.FormData;
var path = formData["path"];
//access files
IList<HttpContent> files = provider.Files;
//Example: reading a file's stream like below
HttpContent file1 = files[0];
Stream file1Stream = await file1.ReadAsStreamAsync();
var loc = Path.Combine(path, cleanupFilename(files[0].Headers.ContentDisposition.FileName));
//var loc = path + "/" + cleanupFilename(files[0].Headers.ContentDisposition.FileName);
_klPackInstallerService.DeployPackage(file1Stream, loc);
return Success();
}
public class InMemoryMultipartFormDataStreamProvider : MultipartStreamProvider
{
private NameValueCollection _formData = new NameValueCollection();
private List<HttpContent> _fileContents = new List<HttpContent>();
// Set of indexes of which HttpContents we designate as form data
private Collection<bool> _isFormData = new Collection<bool>();
/// <summary>
/// Gets a <see cref="NameValueCollection"/> of form data passed as part of the multipart form data.
/// </summary>
public NameValueCollection FormData
{
get { return _formData; }
}
/// <summary>
/// Gets list of <see cref="HttpContent"/>s which contain uploaded files as in-memory representation.
/// </summary>
public List<HttpContent> Files
{
get { return _fileContents; }
}
public override Stream GetStream(HttpContent parent, HttpContentHeaders headers)
{
// For form data, Content-Disposition header is a requirement
ContentDispositionHeaderValue contentDisposition = headers.ContentDisposition;
if (contentDisposition != null)
{
// We will post process this as form data
_isFormData.Add(String.IsNullOrEmpty(contentDisposition.FileName));
return new MemoryStream();
}
// If no Content-Disposition header was present.
throw new InvalidOperationException(string.Format("Did not find required '{0}' header field in MIME multipart body part..", "Content-Disposition"));
}
/// <summary>
/// Read the non-file contents as form data.
/// </summary>
/// <returns></returns>
public override async Task ExecutePostProcessingAsync()
{
// Find instances of non-file HttpContents and read them asynchronously
// to get the string content and then add that as form data
for (int index = 0; index < Contents.Count; index++)
{
if (_isFormData[index])
{
HttpContent formContent = Contents[index];
// Extract name from Content-Disposition header. We know from earlier that the header is present.
ContentDispositionHeaderValue contentDisposition = formContent.Headers.ContentDisposition;
string formFieldName = UnquoteToken(contentDisposition.Name) ?? String.Empty;
// Read the contents as string data and add to form data
string formFieldValue = await formContent.ReadAsStringAsync();
FormData.Add(formFieldName, formFieldValue);
}
else
{
_fileContents.Add(Contents[index]);
}
}
}
/// <summary>
/// Remove bounding quotes on a token if present
/// </summary>
/// <param name="token">Token to unquote.</param>
/// <returns>Unquoted token.</returns>
private static string UnquoteToken(string token)
{
if (String.IsNullOrWhiteSpace(token))
{
return token;
}
if (token.StartsWith("\"", StringComparison.Ordinal) && token.EndsWith("\"", StringComparison.Ordinal) && token.Length > 1)
{
return token.Substring(1, token.Length - 2);
}
return token;
}
}
//TS
DeployPackage(stream: File /*Stream*/, path: string /*String*/): JQueryPromise<void> {
var dfd = $.Deferred<void>();
var data = new FormData();
data.append('SelectedFile', stream);
data.append('path', path);
var request = new XMLHttpRequest();
request.onreadystatechange = () => {
var resp;
if (request.readyState == 4) {
try {
resp = JSON.parse(request.response);
if (resp.Success) {
dfd.resolve();
}
} catch (e) {
resp = {
status: 'error',
data: 'Unknown error occurred: [' + request.responseText + ']'
};
}
log.Error("Uploading File Failed", JSON.stringify(resp));
dfd.reject(resp);
}
};
request.upload.addEventListener('progress', e => {
//Math.ceil(e.loaded / e.total) * 100 + '%';
}, false);
request.open('POST', '/api/AgentService/DeployPackage');
request.send(data);
return dfd.promise();
}
<!--Knockout-->
<input data-bind="event: { change: function() { UploadPackage($element.files[0]) } }" type="file" />
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment