【问题标题】:How to set up a Web API controller for multipart/form-data如何为 multipart/form-data 设置 Web API 控制器
【发布时间】:2015-04-06 19:36:46
【问题描述】:

我正在努力解决这个问题。我的代码没有收到任何有用的错误消息,所以我使用了其他东西来生成一些东西。我在错误消息后附上了该代码。我在上面找到了tutorial,但我不知道如何用我所拥有的来实现它。这是我目前拥有的:

public async Task<object> PostFile()
    {
        if (!Request.Content.IsMimeMultipartContent())
            throw new Exception();


        var provider = new MultipartMemoryStreamProvider();
        var result = new { file = new List<object>() };
        var item = new File();

        item.CompanyName = HttpContext.Current.Request.Form["companyName"];
        item.FileDate = HttpContext.Current.Request.Form["fileDate"];
        item.FileLocation = HttpContext.Current.Request.Form["fileLocation"];
        item.FilePlant = HttpContext.Current.Request.Form["filePlant"];
        item.FileTerm = HttpContext.Current.Request.Form["fileTerm"];
        item.FileType = HttpContext.Current.Request.Form["fileType"];

        var manager = new UserManager<ApplicationUser>(new UserStore<ApplicationUser>(new ApplicationDbContext()));
        var user = manager.FindById(User.Identity.GetUserId());

        item.FileUploadedBy = user.Name;
        item.FileUploadDate = DateTime.Now;

        await Request.Content.ReadAsMultipartAsync(provider)
         .ContinueWith(async (a) =>
         {
             foreach (var file in provider.Contents)
             {
                 if (file.Headers.ContentLength > 1000)
                 {
                     var filename = file.Headers.ContentDisposition.FileName.Trim('\"');
                     var contentType = file.Headers.ContentType.ToString();
                     await file.ReadAsByteArrayAsync().ContinueWith(b => { item.FilePdf = b.Result; });
                 }


             }


         }).Unwrap();

        db.Files.Add(item);
        db.SaveChanges();
        return result;

    }

错误:

Object {message: "该资源不支持请求实体的媒体类型'multipart/form-data'。", exceptionMessage: "没有 MediaTypeFormatter 可用于读取媒体类型为'multipart/form 的对象...om 内容-data'.", exceptionType: "System.Net.Http.UnsupportedMediaTypeException", stackTrace: " at System.Net.Http.HttpContentExtensions.ReadAs...atterLogger, CancellationToken cancelToken)"}exceptionMessage: "没有 MediaTypeFormatter 可用于读取对象来自媒体类型为“multipart/form-data”的内容的“HttpPostedFileBase”类型。”exceptionType:“System.Net.Http.UnsupportedMediaTypeException”消息:“不支持请求实体的媒体类型“multipart/form-data” resource."stackTrace:" at System.Net.Http.HttpContentExtensions.ReadAsAsync[T](HttpContent content, Type type, IEnumerable1 formatters, IFormatterLogger formatterLogger, CancellationToken cancellationToken) ↵ at System.Net.Http.HttpContentExtensions.ReadAsAsync(HttpContent content, Type type, IEnumerable1 formatters, IFormatterLogger formatterLogger, CancellationToken cancelToken)

用于生成错误信息的代码:

    [HttpPost]
    public string UploadFile(HttpPostedFileBase file)
    {

        if (file.ContentLength > 0)
        {
            var fileName = Path.GetFileName(file.FileName);
            var path = Path.Combine(HttpContext.Current.Server.MapPath("~/uploads"), fileName);
            file.SaveAs(path);


        }
        return "/uploads/" + file.FileName;
    }

类:

public class File
{
    public int FileId { get; set; }
    public string FileType { get; set; }
    public string FileDate { get; set; }
    public byte[] FilePdf { get; set; }
    public string FileLocation { get; set; }
    public string FilePlant { get; set; }
    public string FileTerm { get; set; }
    public DateTime? FileUploadDate { get; set; }
    public string FileUploadedBy { get; set; }

    public string CompanyName { get; set; }
    public virtual ApplicationUser User { get; set; }
}

【问题讨论】:

  • 所以看起来您正在将数据从一个后端发布到另一个后端。您使用多部分/表单而不是 JSON 或原始数据的任何原因?
  • 没有理由,这对我有用。我发布了我的角度。我欢迎建议
  • @timothyclifford 为什么不呢? Multipart 是一种有效的方法。此 QA 页面的重点不是建议另一种方法,而是使用您正在寻找的方法解决您遇到的问题,

标签: c# asp.net-web-api multipartform-data


【解决方案1】:

对我来说是这样工作的,希望对你有帮助!

        [HttpPost("AddProduct")]

        public async Task<IActionResult> AddProduct( IFormFile files)
        {
            string fileGuid = Guid.NewGuid().ToString();
            var pathImage = Path.Combine(_hostEnvironment.ContentRootPath, "Images", fileGuid);
            var streamImage = new FileStream(pathImage, FileMode.Append);
            await files.CopyToAsync(streamImage);

            return Ok();

           
        }

【讨论】:

  • 尝试缩进代码
【解决方案2】:

这是针对这个问题的 ASP.Net Core 解决方案的另一个答案...

在 Angular 方面,我采用了这个代码示例...

https://stackblitz.com/edit/angular-drag-n-drop-directive

...并将其修改为调用 HTTP Post 端点:

  prepareFilesList(files: Array<any>) {

    const formData = new FormData();
    for (var i = 0; i < files.length; i++) { 
      formData.append("file[]", files[i]);
    }

    let URL = "https://localhost:44353/api/Users";
    this.http.post(URL, formData).subscribe(
      data => { console.log(data); },
      error => { console.log(error); }
    );

有了这个,这是我在 ASP.Net Core WebAPI 控制器中需要的代码:

[HttpPost]
public ActionResult Post()
{
  try
  {
    var files = Request.Form.Files;

    foreach (IFormFile file in files)
    {
        if (file.Length == 0)
            continue;
        
        string tempFilename = Path.Combine(Path.GetTempPath(), file.FileName);
        System.Diagnostics.Trace.WriteLine($"Saved file to: {tempFilename}");

        using (var fileStream = new FileStream(tempFilename, FileMode.Create))
        {
            file.CopyTo(fileStream);
        }
    }
    return new OkObjectResult("Yes");
  }
  catch (Exception ex)
  {
    return new BadRequestObjectResult(ex.Message);
  }
}

简单得令人震惊,但我必须将几个(几乎正确)来源的示例拼凑起来才能使其正常工作。

【讨论】:

    【解决方案3】:

    5 年后,.NET Core 3.1 允许您像这样指定媒体类型:

    [HttpPost]
    [Consumes("multipart/form-data")]
    public IActionResult UploadLogo()
    {
        return Ok();
    }
    

    【讨论】:

    • 实际上,在 .net 核心中,将 IFormFile 作为参数传递给控制器​​似乎足以让 .net 允许 multipart/form-data(使用 .net 5 测试)。
    • @GyumFox 不是真的,我现在正在 .NET 5 中进行测试并因此而被阻止。
    • 不知道,这对我有用:[HttpPost] public FileResult Post(IFormFile file, string otherArg)
    【解决方案4】:

    也许聚会迟到了。 但有一个替代解决方案是使用ApiMultipartFormFormatter 插件。

    此插件可帮助您像 ASP.NET Core 一样接收 multipart/formdata 内容。

    github页面中已经提供demo。

    【讨论】:

    • 当我发送属性但没有将实际文件发送到 API 控制器的操作时,序列化不会发生,我收到 null 作为我的模型。如果我确实发送文件或从模型中删除文件属性,则序列化工作正常。是按照设计还是我遗漏了什么?
    • @Roman.Pavelko,你能在 git 上创建一个最小的复制吗,最好知道真正发生了什么:)
    • 谢谢。这为我解决了这个问题:stackoverflow.com/questions/68188224/…
    【解决方案5】:

    这解决了我的问题
    将以下行添加到 WebApiConfig.cs

    config.Formatters.XmlFormatter.SupportedMediaTypes.Add(new System.Net.Http.Headers.MediaTypeHeaderValue("multipart/form-data"));
    

    【讨论】:

    • 这只会使 ASP.NET 将 multipart/form-data 请求发送到您的控制器。它无法反序列化表单值并将它们绑定到方法的参数,因为XmlFormatter 无法解析multipart/form-data 有效负载。
    【解决方案6】:

    您收到 HTTP 415“此资源不支持请求实体的媒体类型‘multipart/form-data’。”因为您没有在请求中提及正确的内容类型。

    【讨论】:

    • 想添加更多解释?我不知道如何解决这个问题。
    【解决方案7】:

    检查你的 WebApiConfig 并添加这个

    GlobalConfiguration.Configuration.Formatters.XmlFormatter.SupportedMediaTypes.Clear();
    

    【讨论】:

    • 为什么会突然删除支持的媒体类型?
    • 这个答案毫无意义,甚至不起作用。
    【解决方案8】:

    你可以使用这样的东西

    [HttpPost]
    public async Task<HttpResponseMessage> AddFile()
    {
        if (!Request.Content.IsMimeMultipartContent())
        {
            this.Request.CreateResponse(HttpStatusCode.UnsupportedMediaType);
        }
    
        string root = HttpContext.Current.Server.MapPath("~/temp/uploads");
        var provider = new MultipartFormDataStreamProvider(root);
        var result = await Request.Content.ReadAsMultipartAsync(provider);
    
        foreach (var key in provider.FormData.AllKeys)
        {
            foreach (var val in provider.FormData.GetValues(key))
            {
                if (key == "companyName")
                {
                    var companyName = val;
                }
            }
        }
    
        // On upload, files are given a generic name like "BodyPart_26d6abe1-3ae1-416a-9429-b35f15e6e5d5"
        // so this is how you can get the original file name
        var originalFileName = GetDeserializedFileName(result.FileData.First());
    
        var uploadedFileInfo = new FileInfo(result.FileData.First().LocalFileName);
        string path = result.FileData.First().LocalFileName;
    
        //Do whatever you want to do with your file here
    
        return this.Request.CreateResponse(HttpStatusCode.OK, originalFileName );
    }
    
    private string GetDeserializedFileName(MultipartFileData fileData)
    {
        var fileName = GetFileName(fileData);
        return JsonConvert.DeserializeObject(fileName).ToString();
    }
    
    public string GetFileName(MultipartFileData fileData)
    {
        return fileData.Headers.ContentDisposition.FileName;
    }
    

    【讨论】:

    • 你能告诉我我需要在这里做什么吗?我不太明白
    • 上面的代码将保留在您的接受 multipart/form-data 的 Web API 控制器中。一旦代码验证了它的多部分内容,我们就会得到文件和额外的数据,如“公司名称”,......您可以处理您的文件,保存它并返回所需的结果。您可以使用 angular/.net 代码来访问此方法
    • 我插入了您必须查看的内容,但它在第一个 foreach 循环中引发了错误。部分问题是我不太明白它是如何转换为二进制格式的。我创建了 tmp 文件夹并确保它具有写权限
    • FormData 可能为 null 试试这个 result.FormData.GetValues("companyname").FirstOrDefault();
    • 同样的错误:foreach (var val in result.FormData.GetValues("companyname").FirstOrDefault())
    【解决方案9】:

    我通常只在 Mvc 控制器 中使用 HttpPostedFileBase 参数。在处理 ApiControllers 时,请尝试检查 HttpContext.Current.Request.Files 属性以获取传入文件:

    [HttpPost]
    public string UploadFile()
    {
        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);
        }
    
        return file != null ? "/uploads/" + file.FileName : null;
    }
    

    【讨论】:

    • 这是错误信息。找不到路径“C:\Development\Transp\temp\uploads\02-Unicare.PNG”的一部分。
    • 权限是通过windows文件资源管理器设置的吗?
    • 现在您用来保存接收到的文件的路径似乎无效。检查该文件夹是否存在,并且您的应用程序对该文件夹具有读/写权限(是的,您可以使用 Windows 资源管理器执行此操作)。
    • 我将文件夹更改为 App_Start 并且成功了!现在这实际上是将文件存储在服务器上。如果我这样做,我如何将路径和其他属性存储在数据库中?
    • 我很高兴它成功了。关于您的新疑问,我建议您在 StackOverflow 中创建一个新问题,而不是更新此问题,以显示您如何尝试将内容保存在数据库中,以及为什么它不起作用。
    猜你喜欢
    • 1970-01-01
    • 2020-01-08
    • 2019-11-15
    • 2019-06-22
    • 1970-01-01
    • 2015-09-19
    • 1970-01-01
    • 2014-10-31
    • 2019-08-28
    相关资源
    最近更新 更多