您当前的位置: 首页 >  .net

寒冰屋

暂无认证

  • 0浏览

    0关注

    2286博文

    0收益

  • 0浏览

    0点赞

    0打赏

    0留言

私信
关注
热门博文

如何在ASP.NET Core中上传文档

寒冰屋 发布时间:2020-02-10 16:58:35 ,浏览量:0

目录

介绍

问题

解决方案

秘密调味料成分1:IFormFile

秘密调味料成分2:IFormFile参数名称

秘密调味料成分#3:FromForm参数属性

秘密调味料成分4:使用表单元素实例化FormData

源代码

客户端

服务器端

CORS

控制器代码

运行代码

  • 下载演示-4.1 KB
介绍

这个上周末,我可能花了6到7个小时来弄清楚如何将文档上载到ASP.NET Core应用程序。尽管有很多示例,但它们并不能满足我的要求,更糟糕的是,还需要一定数量的魔咒,而实际上却没有人花时间去解释。我的意思是没人。我在StackOverflow上发现了一个模糊的响应,它导致了一个问题的解决,另一个问题在Mozilla的FormData站点上。数小时后,“为什么这在Postman中起作用,但在我简单的网站上却不起作用?” 我终于有了一个可行的解决方案。

因此,这篇简短的文章的重点是描述神奇的咒语,这样您就不必经历我的痛苦。也许这对您来说很明显,但是直到现在,实际的可行解决方案还是不存在。

那我的问题是什么?你到底怎么了,马克?

问题

上载文档的常用方法是使用form标签和一个附带的submit按钮。该form标签需要一个带有指向上载终结点的URL的action属性。

为什么不?因为我不想对action属性大惊小怪,而我想使用包装在Promise中的XMLHttpRequest,所以我可以处理响应(在我的例子中,就是上载文档的ID)并捕获异常。此外,标准表单提交会执行重定向,尽管可以通过返回NoContent()来停止重定向,但这是一个令人毛骨悚然的麻烦。当然,您不需要“提交”按钮,可以有一个单独的按钮进行调用form.submit(),这也很棒。除了我还想添加不一定是form数据包一部分的键值对,是的,我在那里发现的纠缠涉及隐藏input元素或创建整个form元素及其子元素。

解决方案

一旦弄清了秘密的调味料,解决方案当然很简单。

秘密调味料成分1:IFormFile

因此,.NET Core具有此接口IFormFile,您可以使用该接口将文档流式传输到客户端。但是您不能随便写这样的端点:

public async Task UploadDocument(IFormFile fileToUpload)
秘密调味料成分2:IFormFile参数名称

参数名称必须与HTML 中的name属性值匹配!因此,如果您的HTML看起来像这样:

您的端点必须用file作参数名称:

public async Task UploadDocument(IFormFile file)

“file”与“file”匹配。

您还可以执行以下操作:

public async Task UploadDocument(DocumentUpload docInfo)

而在类DocumentUpload上你有这个:

public class DocumentUpload
{
    public IFormFile File { get; set; }
}

在此,“File”与“file”匹配。

多个文件也有不同的变体,如List也被支持。

秘密调味料成分#3:FromForm参数属性

上面的例子不起作用!那是因为我们需要C#属性FromForm,所以这是您正确编写端点(使用类版本)的方式:

public async Task UploadDocument([FromForm] DocumentUpload docInfo)
秘密调味料成分4:使用表单元素实例化FormData

因此,在客户端,我们需要这样做:

let formData = new FormData(form);

form来自这样的代码:document.getElementById("uploadForm");

烦人的是,我遇到了许多例子,人们说这行得通:

let formData = new FormData();
formData.append("file", valueFromInputElement);

这行不通!!!

源代码

因此,这是完整的源代码。

客户端



    
    Upload Demo


    
        html.wait, html.wait * {
            cursor: wait !important;
        }
    

    
        
Upload function doUpload() { let form = document.getElementById("uploadForm"); Upload("http://localhost:60192/UploadDocument", form, { clientDate: Date() }) .then(xhr => alert(xhr.response)) .catch(xhr => alert(xhr.statusText)); } async function Upload(url, form, extraData) { waitCursor(); let xhr = new XMLHttpRequest(); return new Promise((resolve, reject) => { xhr.onreadystatechange = () => { if (xhr.readyState == 4) { if (xhr.status >= 200 && xhr.status < 300) { readyCursor(); resolve(xhr); } else { readyCursor(); reject(xhr); } } }; xhr.open("POST", url, true); let formData = new FormData(form); Object.entries(extraData).forEach(([key, value]) => formData.append(key, value)); xhr.send(formData); }); } function waitCursor() { document.getElementsByTagName("html")[0].classList.add("wait"); } function readyCursor() { document.getElementsByTagName("html")[0].classList.remove("wait"); }

注意事项:

  1. 我已经硬编码"http://localhost:60192/UploadDocument",您可能需要更改端口。
  2. 请注意formData.append(key, value));,这是我要添加不属于form的键/值对的地方。
  3. 没有“提交”按钮,而是有一个单独的Upload按钮。

就像我说的那样,简单!

服务器端

我是在VS2019中编写的代码,因此我们使用的是.NET Core 3.1,因此让我们先介绍一些调整。

CORS

必须添加允许跨域发布的功能,因为ASP.NET Core服务器不为该页面提供服务,我只是将其直接加载到Chrome中。因此,请求的“来源”不是来自“服务器”。在Startup类中,我添加了AddCors服务。

public void ConfigureServices(IServiceCollection services)
{
    services.AddControllers();

    services.AddCors(options => {
        options.AddPolicy("CorsPolicy",
            builder => builder.AllowAnyOrigin()
            .AllowAnyMethod()
            .AllowAnyHeader());
    });
}

并将其应用于:

public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{
    app.UseCors("CorsPolicy");

其必须在app调用之前完成。说真的,我阅读了有关中间件管道的说明,但是我必须说,WTF?为什么会有初始化顺序问题?

控制器代码
using System.IO;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Mvc;

namespace UploadDemo.Controllers
{
    public class DocumentUpload
    {
        public string Description { get; set; }
        public IFormFile File { get; set; }
        public string ClientDate { get; set; }
    }

    [ApiController]
    [Route("")]                                                     
    public class UploadController : ControllerBase
    {
        public UploadController()
        {
        }

        [HttpGet]
        public ActionResult Hello()
        {
            return "Hello World!";
        }

        [HttpPost]
        [Route("UploadDocument")]
        public async Task UploadDocument([FromForm] DocumentUpload docInfo)
        {
            IFormFile iff = docInfo.File;
            string fn = iff.FileName;
            var tempFilename = $@"c:\temp\{fn}";

            using (var fileStream = new FileStream(tempFilename, FileMode.Create))
            {
                await iff.CopyToAsync(fileStream);
            }

            return Ok($"File {fn} uploaded.  
                   Description = {docInfo.Description} on {docInfo.ClientDate}");
        }
    }
}

注意事项:

  1. 请注意,控制器路由是""因为我不在乎URL中的路径片段。
  2. 我假设您有c:\temp文件夹。毕竟,这是一个演示!
运行代码

运行ASP.NET Core应用程序。它将启动一个浏览器实例:

任何提示都不要关闭它,只是忽略它。

接下来,打开项目文件夹中的“index.html”文件,您应该看到:

选择一个文件,输入描述,然后单击“Upload”按钮,您应该会看到如下所示的警报——当然,响应会有所不同,因为您输入的内容与我不同:

您应该在temp文件夹中注意到您上传的文件:

当然不是那个文件。但是,在找出所有秘密调味料之后,我几乎看起来像这样!

关注
打赏
1665926880
查看更多评论
立即登录/注册

微信扫码登录

0.0461s