使用额外参数上传Webapi formdata(到DB)
我需要上传文件发送额外的参数。
我在stackoverflow中找到了以下post: 带有额外参数的Webapi ajax formdata上传
它描述了如何使用MultipartFormDataStreamProvider并将数据保存到文件服务器。 我不需要将文件保存到服务器,而是保存到DB。 我已经使用MultipartMemoryStreamProvider处理代码,但它不使用额外的参数。
你能告诉我如何在webapi中处理额外的参数吗?
例如,如果我添加文件并测试参数:
data.append("myParameter", "test");
这是我的webapi处理fileupload而没有额外的参数:
if (Request.Content.IsMimeMultipartContent()) { var streamProvider = new MultipartMemoryStreamProvider(); var task = Request.Content.ReadAsMultipartAsync(streamProvider).ContinueWith<IEnumerable>(t => { if (t.IsFaulted || t.IsCanceled) { throw new HttpResponseException(HttpStatusCode.InternalServerError); } _fleDataService = new FileDataBLL(); FileData fle; var fleInfo = streamProvider.Contents.Select(i => { fle = new FileData(); fle.FileName = i.Headers.ContentDisposition.FileName; var contentTest = i.ReadAsByteArrayAsync(); contentTest.Wait(); if (contentTest.Result != null) { fle.FileContent = contentTest.Result; } // get extra parameters here ?????? _fleDataService.Save(fle); return new FileModel(i.Headers.ContentDisposition.FileName, 1024); //todo }); return fleInfo; }); return task; }
您可以通过实现自定义DataStreamProvider
以非常非常干净的方式实现此目的,该自定义DataStreamProvider
复制用于从MultipartFormDataStreamProvider
多部分内容中解析FormData的逻辑。
我不太确定为什么决定从MultiPartFileStreamProvider
inheritanceMultipartFormDataStreamProvider
而不至少提取标识和公开FormData集合的代码,因为它对于涉及多部分数据的许多任务非常有用,只需将文件保存到磁盘。
无论如何,以下提供商应该有助于解决您的问题。 您仍然需要确保在迭代提供程序内容时忽略任何没有文件名的内容(特别是语句streamProvider.Contents.Select()
否则您可能会尝试将formdata上传到数据库)。 因此,询问提供者的代码是一个HttpContent IsStream(),这有点像黑客,但最简单的是我能想到这样做。
请注意,它基本上是来自MultipartFormDataStreamProvider
源的剪切和粘贴斧头作业 – 它尚未经过严格测试(受此答案的启发)。
public class MultipartFormDataMemoryStreamProvider : MultipartMemoryStreamProvider { private readonly Collection _isFormData = new Collection (); private readonly NameValueCollection _formData = new NameValueCollection(StringComparer.OrdinalIgnoreCase); public NameValueCollection FormData { get { return _formData; } } 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) { _isFormData.Add(String.IsNullOrEmpty(contentDisposition.FileName)); return base.GetStream(parent, headers); } throw new InvalidOperationException("Did not find required 'Content-Disposition' header field in MIME multipart body part."); } public override async Task ExecutePostProcessingAsync() { for (var index = 0; index < Contents.Count; index++) { if (IsStream(index)) continue; var formContent = Contents[index]; var contentDisposition = formContent.Headers.ContentDisposition; var formFieldName = UnquoteToken(contentDisposition.Name) ?? string.Empty; var formFieldValue = await formContent.ReadAsStringAsync(); FormData.Add(formFieldName, formFieldValue); } } 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; } public bool IsStream(int idx) { return !_isFormData[idx]; } }
它可以如下使用(使用TPL语法来匹配您的问题):
[HttpPost] public Task Post() { if (!Request.Content.IsMimeMultipartContent()) throw new HttpResponseException(Request.CreateResponse(HttpStatusCode.NotAcceptable, "Invalid Request!")); var provider = new MultipartFormDataMemoryStreamProvider(); return Request.Content.ReadAsMultipartAsync(provider).ContinueWith(p => { var result = p.Result; var myParameter = result.FormData.GetValues("myParameter").FirstOrDefault(); foreach (var stream in result.Contents.Where((content, idx) => result.IsStream(idx))) { var file = new FileData(stream.Headers.ContentDisposition.FileName); var contentTest = stream.ReadAsByteArrayAsync(); // ... and so on, as per your original code. } return myParameter; }); }
我使用以下HTML表单对其进行了测试:
扩展gooid的答案,我将FormData提取封装到提供程序中,因为我遇到引用它的问题。 在我看来,这只是提供了一个更好的实现。
public class MultipartFormDataMemoryStreamProvider : MultipartMemoryStreamProvider { private readonly Collection _isFormData = new Collection (); private readonly NameValueCollection _formData = new NameValueCollection(StringComparer.OrdinalIgnoreCase); private readonly Dictionary _fileStreams = new Dictionary(); public NameValueCollection FormData { get { return _formData; } } public Dictionary 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; } }
这就是我如何使用它。 请注意,我使用等待,因为我们在.NET 4.5上。
[HttpPost] public async Task 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 uploadedFiles = new List (); foreach (KeyValuePair 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)); }
我真的需要上传文件的媒体类型和长度,所以我修改了@Mark Seefeldt对以下内容的回答:
public class MultipartFormFile { public string Name { get; set; } public long? Length { get; set; } public string MediaType { get; set; } public Stream Stream { get; set; } } public class MultipartFormDataMemoryStreamProvider : MultipartMemoryStreamProvider { private readonly Collection _isFormData = new Collection (); private readonly NameValueCollection _formData = new NameValueCollection(StringComparer.OrdinalIgnoreCase); private readonly List _fileStreams = new List (); public NameValueCollection FormData { get { return _formData; } } public List 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 var file = new MultipartFormFile { Name = UnquoteToken(formContent.Headers.ContentDisposition.FileName), Length = formContent.Headers.ContentLength, MediaType = formContent.Headers.ContentType.MediaType, Stream = await formContent.ReadAsStreamAsync() }; FileStreams.Add(file); } } } 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; } }
最终,以下是对我有用的:
string root = HttpContext.Current.Server.MapPath("~/App_Data"); var provider = new MultipartFormDataStreamProvider(root); var filesReadToProvider = await Request.Content.ReadAsMultipartAsync(provider); foreach (var file in provider.FileData) { var fileName = file.Headers.ContentDisposition.FileName.Replace("\"", string.Empty); byte[] documentData; documentData = File.ReadAllBytes(file.LocalFileName); DAL.Document newRecord = new DAL.Document { PathologyRequestId = PathologyRequestId, FileName = fileName, DocumentData = documentData, CreatedById = ApplicationSecurityDirector.CurrentUserGuid, CreatedDate = DateTime.Now, UpdatedById = ApplicationSecurityDirector.CurrentUserGuid, UpdatedDate = DateTime.Now }; context.Documents.Add(newRecord); context.SaveChanges(); }