Expanding on gooid's answer, I encapsulated the FormData extraction into the provider because I was having issues with it being quoted. This just provided a better implementation in my opinion.
public class MultipartFormDataMemoryStreamProvider : MultipartMemoryStreamProvider
{
private readonly Collection<bool> _isFormData = new Collection<bool>();
private readonly NameValueCollection _formData = new NameValueCollection(StringComparer.OrdinalIgnoreCase);
private readonly Dictionary<string, Stream> _fileStreams = new Dictionary<string, Stream>();
public NameValueCollection FormData
{
get { return _formData; }
}
public Dictionary<string, Stream> FileStreams
{
get { return _fileStreams; }
}
public override Stream GetStream(HttpContent parent, HttpContentHeaders headers)
{
if (parent == null)
{
throw new ArgumentNullException("parent");
}
if (headers == null)
{
throw new ArgumentNullException("headers");
}
var contentDisposition = headers.ContentDisposition;
if (contentDisposition == null)
{
throw new InvalidOperationException("Did not find required 'Content-Disposition' header field in MIME multipart body part.");
}
_isFormData.Add(String.IsNullOrEmpty(contentDisposition.FileName));
return base.GetStream(parent, headers);
}
public override async Task ExecutePostProcessingAsync()
{
for (var index = 0; index < Contents.Count; index++)
{
HttpContent formContent = Contents[index];
if (_isFormData[index])
{
// Field
string formFieldName = UnquoteToken(formContent.Headers.ContentDisposition.Name) ?? string.Empty;
string formFieldValue = await formContent.ReadAsStringAsync();
FormData.Add(formFieldName, formFieldValue);
}
else
{
// File
string fileName = UnquoteToken(formContent.Headers.ContentDisposition.FileName);
Stream stream = await formContent.ReadAsStreamAsync();
FileStreams.Add(fileName, stream);
}
}
}
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;
}
}
And here's how I'm using it. Note that I used await since we're on .NET 4.5.
[HttpPost]
public async Task<HttpResponseMessage> Upload()
{
if (!Request.Content.IsMimeMultipartContent())
{
return Request.CreateResponse(HttpStatusCode.UnsupportedMediaType, "Unsupported media type.");
}
// Read the file and form data.
MultipartFormDataMemoryStreamProvider provider = new MultipartFormDataMemoryStreamProvider();
await Request.Content.ReadAsMultipartAsync(provider);
// Extract the fields from the form data.
string description = provider.FormData["description"];
int uploadType;
if (!Int32.TryParse(provider.FormData["uploadType"], out uploadType))
{
return Request.CreateResponse(HttpStatusCode.BadRequest, "Upload Type is invalid.");
}
// Check if files are on the request.
if (!provider.FileStreams.Any())
{
return Request.CreateResponse(HttpStatusCode.BadRequest, "No file uploaded.");
}
IList<string> uploadedFiles = new List<string>();
foreach (KeyValuePair<string, Stream> file in provider.FileStreams)
{
string fileName = file.Key;
Stream stream = file.Value;
// Do something with the uploaded file
UploadManager.Upload(stream, fileName, uploadType, description);
// Keep track of the filename for the response
uploadedFiles.Add(fileName);
}
return Request.CreateResponse(HttpStatusCode.OK, "Successfully Uploaded: " + string.Join(", ", uploadedFiles));
}
与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…