接受文件上传和附加参数的 WebAPI 方法
Posted
技术标签:
【中文标题】接受文件上传和附加参数的 WebAPI 方法【英文标题】:WebAPI method that takes a file upload and additional arguments 【发布时间】:2014-07-06 22:10:44 【问题描述】:我想上传一个文件并随文件一起发送一些附加信息,比如说一个字符串 foo 和一个 int bar。
如何编写接收文件上传、字符串和 int 的 ASP.NET WebAPI 控制器方法?
我的 javascript:
var fileInput = document.querySelector("#filePicker");
var formData = new FormData();
formData.append("file", fileInput.files[0]);
formData.append("foo", "hello world!");
formData.append("bar", 42);
var options =
url: "/api/foo/upload",
data: formData,
processData: false // Prevents JQuery from transforming the data into a query string
;
$.ajax(options);
我的 WebAPI 控制器可以像这样访问文件:
public async Task<HttpResponseMessage> Upload()
var streamProvider = new MultipartMemoryStreamProvider();
await Request.Content.ReadAsMultipartAsync(streamProvider);
var fileStream = await streamProvider.Contents[0].ReadAsStreamAsync();
但我不清楚如何获取字符串和 int。我想我可能会说 streamProvider.Content[1] 或其他什么,但这感觉超级讨厌。
编写接受文件上传、字符串和 int 的 WebAPI 操作的正确方法是什么?
【问题讨论】:
我可能遗漏了一些东西...为什么不能只为 Upload 方法设置 foo 和 bar 参数并让绑定魔法发生? 我专注于在 FormData 中发送数据(例如,作为 $.post(url, formData) 中的数据;模型绑定器永远找不到它们。结果,我可以发布文件单独作为表单数据,然后将其他参数放在 URL 中。然后模型绑定器发挥其魔力。 【参考方案1】:您可以创建自己的MultipartFileStreamProvider
来访问其他参数。
在ExecutePostProcessingAsync
中,我们以多部分形式循环遍历每个文件并加载自定义数据(如果您只有一个文件,那么CustomData
列表中将只有一个对象)。
class MyCustomData
public int Foo get; set;
public string Bar get; set;
class CustomMultipartFileStreamProvider : MultipartMemoryStreamProvider
public List<MyCustomData> CustomData get; set;
public CustomMultipartFileStreamProvider()
CustomData = new List<MyCustomData>();
public override Task ExecutePostProcessingAsync()
foreach (var file in Contents)
var parameters = file.Headers.ContentDisposition.Parameters;
var data = new MyCustomData
Foo = int.Parse(GetNameHeaderValue(parameters, "Foo")),
Bar = GetNameHeaderValue(parameters, "Bar"),
;
CustomData.Add(data);
return base.ExecutePostProcessingAsync();
private static string GetNameHeaderValue(ICollection<NameValueHeaderValue> headerValues, string name)
var nameValueHeader = headerValues.FirstOrDefault(
x => x.Name.Equals(name, StringComparison.OrdinalIgnoreCase));
return nameValueHeader != null ? nameValueHeader.Value : null;
然后在你的控制器中:
class UploadController : ApiController
public async Task<HttpResponseMessage> Upload()
var streamProvider = new CustomMultipartFileStreamProvider();
await Request.Content.ReadAsMultipartAsync(streamProvider);
var fileStream = await streamProvider.Contents[0].ReadAsStreamAsync();
var customData = streamProvider.CustomData;
return Request.CreateResponse(HttpStatusCode.Created);
【讨论】:
谢谢。我最终只是使用“简单”参数传递了参数,例如$.post("/api/foo?id=23&bar=hello", fileData).那行得通..前提是您没有传递复杂的对象或数组。我更喜欢你的解决方案,所以我将其标记为答案。 在我们的尝试中,我们在键/值对的值部分获取参数的名称 - 键字段简单包含“名称”。自 5 月以来有什么变化吗?【参考方案2】:我认为这里的答案非常好。因此,其他人可以看到一个简单的示例,说明如何以摘要形式传递除了文件之外的数据,其中包括一个 Javascript 函数,该函数使 WebAPI 调用 FileUpload 控制器,以及来自 FileUpload 控制器的 sn-p(在 VB. net) 读取从 Javascript 传递的附加数据。
Javascript:
function uploadImage(files)
var data = new FormData();
if (files.length > 0)
data.append("UploadedImage", files[0]);
data.append("Source", "1")
var ajaxRequest = $.ajax(
type: "POST",
url: "/api/fileupload/uploadfile",
contentType: false,
processData: false,
data: data
);
文件上传控制器:
<HttpPost> _
Public Function UploadFile() As KeyValuePair(Of Boolean, String)
Try
If HttpContext.Current.Request.Files.AllKeys.Any() Then
Dim httpPostedFile = HttpContext.Current.Request.Files("UploadedImage")
Dim source = HttpContext.Current.Request.Form("Source").ToString()
所以在Javascript中可以看到,传递的附加数据是“Source”键,值是“1”。正如 Chandrika 上面回答的那样,控制器通过“System.Web.HttpContext.Current.Request.Form("Source").ToString()”读取这个传递的数据。
请注意,Form("Source") 使用 ()(与 [])作为控制器代码在 VB.net 中。
希望这会有所帮助。
【讨论】:
【参考方案3】:您可以通过这种方式提取多个文件和多个属性:
public async Task<HttpResponseMessage> Post()
Dictionary<string,string> attributes = new Dictionary<string, string>();
Dictionary<string, byte[]> files = new Dictionary<string, byte[]>();
var provider = new MultipartMemoryStreamProvider();
await Request.Content.ReadAsMultipartAsync(provider);
foreach (var file in provider.Contents)
if (file.Headers.ContentDisposition.FileName != null)
var filename = file.Headers.ContentDisposition.FileName.Trim('\"');
var buffer = await file.ReadAsByteArrayAsync();
files.Add(filename, buffer);
else
foreach(NameValueHeaderValue p in file.Headers.ContentDisposition.Parameters)
string name = p.Value;
if (name.StartsWith("\"") && name.EndsWith("\"")) name = name.Substring(1, name.Length - 2);
string value = await file.ReadAsStringAsync();
attributes.Add(name, value);
//Your code here
return new HttpResponseMessage(HttpStatusCode.OK);
【讨论】:
【参考方案4】:var receipents = HttpContext.Current.Request.Params["Receipents"]; var 参与者 = HttpContext.Current.Request.Params["Participants"];
var file = HttpContext.Current.Request.Files.Count > 0 ? HttpContext.Current.Request.Files[0] : null;
if (file != null && file.ContentLength > 0)
var fileName = Path.GetFileName(file.FileName);
var path = Path.Combine(
HttpContext.Current.Server.MapPath("~/uploads"),
fileName
);
file.SaveAs(path);
【讨论】:
将参数和文件放在一起的最简单解决方案。 一般来说,如果答案包含对代码的用途的解释,以及为什么在不介绍其他人的情况下解决问题的原因,答案会更有帮助。【参考方案5】:您可以通过以下方式做到这一点: jQuery 方法:
var data = new FormData();
data.append("file", filesToUpload[0].rawFile);
var doc = ;
doc.DocumentId = 0;
$.support.cors = true;
$.ajax(
url: '/api/document/uploaddocument',
type: 'POST',
contentType: 'multipart/form-data',
data: data,
cache: false,
contentType: false,
processData: false,
success: function (response)
docId = response.split('|')[0];
doc.DocumentId = docId;
$.post('/api/document/metadata', doc)
.done(function (response)
);
alert('Document save successfully!');
,
error: function (e)
alert(e);
);
调用您的“UploadDocuement”网络 API
[Route("api/document/uploaddocument"), HttpPost]
[UnhandledExceptionFilter]
[ActionName("UploadDocument")]
public Task<HttpResponseMessage> UploadDocument()
// Check if the request contains multipart/form-data.
if (!Request.Content.IsMimeMultipartContent())
Task<HttpResponseMessage> mytask = new Task<HttpResponseMessage>(delegate()
return new HttpResponseMessage()
StatusCode = HttpStatusCode.BadRequest,
Content = "In valid file & request content type!".ToStringContent()
;
);
return mytask;
string root = HttpContext.Current.Server.MapPath("~/Documents");
if (System.IO.Directory.Exists(root))
System.IO.Directory.CreateDirectory(root);
var provider = new MultipartFormDataStreamProvider(root);
var task = Request.Content.ReadAsMultipartAsync(provider).
ContinueWith<HttpResponseMessage>(o =>
if (o.IsFaulted || o.IsCanceled)
throw new HttpResponseException(HttpStatusCode.InternalServerError);
FileInfo finfo = new FileInfo(provider.FileData.First().LocalFileName);
string guid = Guid.NewGuid().ToString();
File.Move(finfo.FullName, Path.Combine(root, guid + "_" + provider.FileData.First().Headers.ContentDisposition.FileName.Replace("\"", "")));
string sFileName = provider.FileData.First().Headers.ContentDisposition.FileName.Replace("\"", "");
FileInfo FInfos = new FileInfo(Path.Combine(root, guid + "_" + provider.FileData.First().Headers.ContentDisposition.FileName.Replace("\"", "")));
Document dbDoc = new Document()
DocumentID = 0
;
context.DocumentRepository.Insert(dbDoc);
context.Save();
return new HttpResponseMessage()
Content = new StringContent(string.Format("0|File uploaded.", dbDoc.DocumentID))
;
);
return task;
通过以下方式调用您的元数据 Web api:
[Route("api/document/metadata"), HttpPost]
[ActionName("Metadata")]
public Task<HttpResponseMessage> Metadata(Document doc)
int DocId = Convert.ToInt32(System.Web.HttpContext.Current.Request.Form["DocumentId"].ToString());
Task<HttpResponseMessage> mytask = new Task<HttpResponseMessage>(delegate()
return new HttpResponseMessage()
Content = new StringContent("metadata updated")
;
);
return mytask;
【讨论】:
所以,如果我理解正确,您将进行 2 次调用:一个用于文件,另一个用于元数据(例如,我的问题的“附加参数”部分)。有没有办法在一次通话中做到这一点? 在 UploadDocument web api "System.Web.HttpContext.Current.Request.Form["DocumentId"].ToString()" 中试试这个 在上面的示例中,我进行了 2 个调用,一个用于文件,另一个用于元数据,您可以通过一个调用来获取它,如下所示在 UploadDocument web api "System.Web.HttpContext.Current .Request.Form["DocumentId"].ToString()"以上是关于接受文件上传和附加参数的 WebAPI 方法的主要内容,如果未能解决你的问题,请参考以下文章