Infragistics IgniteUI IGFileUpload 不会将文件上传到 ASP.NET MVC CORE 3.0 中的服务器

Infragistics IgniteUI IGFileUpload doesn't upload files to the server in ASP.NET MVC CORE 3.0

假设我有一个简单的 Index.cshtml 视图:

<link href="~/libs/JQueryUI/jquery-ui.css" rel="stylesheet" />
<link href="~/libs/infragistics/css/themes/infragistics/infragistics.theme.css" rel="stylesheet" />
<link href="~/libs/infragistics/css/structure/infragistics.css" rel="stylesheet" />

@using CommonLib.Source.Common.Converters
@using CommonLib.Source.Common.Extensions
@using Infragistics.Web.Mvc
@using Microsoft.AspNetCore.Http
@{
    ViewData["Title"] = "Index";
}

<h1>Index</h1>

<div id="chart" style="color: blue"></div>

<div id="indicator"></div>

<div id="igFileUpload" style="color: blue"></div>

<script src="~/libs/JQuery/jquery.js"></script>
<script src="~/libs/JQueryUI/jquery-ui.js"></script>
<script src="~/libs/infragistics/js/infragistics.core.js"></script>
<script src="~/libs/infragistics/js/infragistics.lob.js"></script>
<script src="~/libs/Lightweight-Charts/dist/lightweight-charts.standalone.development.js"></script>
<script src="~/MyScripts.js"></script>

@(
    Html.Infragistics().Upload()
        .ID("igFileUpload")
        .Mode(UploadMode.Multiple)
        .AutoStartUpload(true)
        .ProgressUrl(Context.GetAbsoluteUrl("~/IGUploadStatusHandler.ashx"))
        .UploadUrl(Context.GetAbsoluteUrl("~/Data/UploadedFiles")) // just use Url.Content for testing purposes
        .ControlId("serverID1")
        .Width("600px")
        .Render()
)

<div id="error-message" style="color: #FF0000; font-weight: bold;"></div>

<script type="text/javascript">
    $(function () {
        $("#igFileUpload").bind({ iguploadonerror: function (e, args) {
                $("#error-message").html(args.errorMessage).stop(true, true).fadeIn(500).delay(3000).fadeOut(500);
            }
        });
    });
</script>

我还得到了Startup.cs个文件:

using System;
using System.IO;
using CommonLib.Source.Common.Utils;
using Infragistics.Web.Mvc;
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Hosting;
using Microsoft.AspNetCore.Http;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.FileProviders;
using Microsoft.Extensions.Hosting;

namespace CryptoBotCoreMVC
{
    public class Startup
    {
        public void ConfigureServices(IServiceCollection services)
        {
            services.AddSingleton<IHttpContextAccessor, HttpContextAccessor>();
            services.AddMvc(option => option.EnableEndpointRouting = false);
            //services.Configure<UploadAppSettings>(options =>
            //{
            //    options.FileUploadPath = $@"{Directory.GetCurrentDirectory()}\Data"; //WebUtils.GetWebAppAbsolutePath("
            //}); // TODO: fu doesn't work | FileUploadPath is set in the View because it is too early to do it here
        }

        public void Configure(IApplicationBuilder app, IWebHostEnvironment env, IServiceProvider svp)
        {
            WebUtils.Configure(app.ApplicationServices.GetRequiredService<IHttpContextAccessor>(), env); 
            if (env.IsDevelopment())
                app.UseDeveloperExceptionPage();

            app.UseStaticFiles();
            app.UseRouting();
            app.UseUploadModuleMiddleware();
            app.UseUploadHandlerMiddleware();
            app.UseFileServer(new FileServerOptions
            {
                FileProvider = new PhysicalFileProvider($@"{Directory.GetCurrentDirectory()}\Data"),
                RequestPath = "/Data",
                EnableDirectoryBrowsing = true
            });

            app.UseMvc(routes =>
            {
                routes.MapRoute("default", "{controller=Home}/{action=Index}/{id?}");
            });
        }
    }
}

此处参考: https://www.igniteui.com/help/using-igniteui-controls-in-asp.net-core-1.0-project

为了安全起见,我在 web.config:

中设置了最大文件大小
<security>
  <requestFiltering>
    <requestLimits maxAllowedContentLength="1073741824" /> <!-- 1 GB -->
  </requestFiltering>
</security>

在 IIS 中:

这是 Fiddler 显示的内容:

第一名:

第二和第三:

据此:
https://www.igniteui.com/help/igupload-using-http-handler-and-modules
错误的意思是:

Status: 3 - File not found - this status is used when it is not found such key in the dictionary
Error: 5 - File does not exist with the specified key in the request

这是结果本身:

这里是控件文档的参考(其中一些不适用于ASP.NET CORE):

https://www.igniteui.com/help/igupload-igupload
https://www.igniteui.com/file-upload/overview

问题是,如您所见,虽然控件有效,但文件实际上并未上传到服务器。我不认为这是一个错误,我认为我对 CORE 3.0 不够熟悉并且我在 IGUploadStatusHandler.ashx.
的配置中遗漏了一些相当明显的东西 如果可能的话,我宁愿避免挖掘 IF 默认 Web 处理程序代码。

/编辑

根据@MasLoo 的建议,我实现了中间件来替换显然需要的 IgnoreRoute,但我不明白直接从中抛出 404 如何使处理程序工作:

(...)
app.UseMiddleware<IgnoreRouteMiddleware>();
(...)

:

using System.Threading.Tasks;
using Microsoft.AspNetCore.Http;

namespace CryptoBotCoreMVC.Middlewares
{
    public class IgnoreRouteMiddleware
    {
        private readonly RequestDelegate _next;

        public IgnoreRouteMiddleware(RequestDelegate next)
        {
            _next = next;
        }

        public async Task Invoke(HttpContext context)
        {
            if (context.Request.Path.HasValue && context.Request.Path.Value.Contains("IGUploadStatusHandler.ashx"))
            {
                context.Response.StatusCode = 404;
                return;
            }

            await _next.Invoke(context);
        }
    }
}

测试后确实立即抛出 404,实际上是两次:

关于评论:

//context.Response.StatusCode = 404;

提琴手抓住了这个:

带有以下消息:

我没有 post 消息,因为它们与第一条消息完全相同 Content-Length: 0

热烈欢迎进一步的想法。

// 编辑 2

我想我快到了,但还不完全是。我注意到,出于某种原因,如果我保留默认的 url,IF 代码不会在 UploadUrl 中查找文件的文件夹,而是查找 ig_fua34sf345sdf13sdf3454erdsf2345asd3425df5235d54df345.aspx 路径,所以我将 HtmlHelper 更改为:

Html.Infragistics().Upload()
    .ID("igFileUpload")
    .Mode(UploadMode.Multiple)
    .AutoStartUpload(true)
    .ProgressUrl(Context.GetAbsoluteUrl("~/IGUploadStatusHandler.ashx"))
    .UploadUrl(Context.GetAbsoluteUrl("~/ig_fua34sf345sdf13sdf3454erdsf2345asd3425df5235d54df345.aspx"))
    .ControlId("serverID1")
    .Width("600px")
    .Render()

现在我得到了 500s:

具有以下堆栈跟踪:

HTTP/1.1 500 Internal Server Error
Content-Type: text/plain
Server: Microsoft-IIS/10.0
X-Powered-By: ASP.NET
Date: Thu, 21 Nov 2019 00:17:16 GMT
Content-Length: 1398

System.InvalidOperationException: Synchronous operations are disallowed. Call ReadAsync or set AllowSynchronousIO to true instead.
   at Microsoft.AspNetCore.Server.IIS.Core.HttpRequestStream.Read(Byte[] buffer, Int32 offset, Int32 count)
   at Microsoft.AspNetCore.Server.IIS.Core.WrappingStream.Read(Byte[] buffer, Int32 offset, Int32 count)
   at Infragistics.Web.Mvc.MiddleWare.UploadModuleMiddleware.ReadFully(Stream input)
   at Infragistics.Web.Mvc.MiddleWare.UploadModuleMiddleware.Invoke(HttpContext context)
   at CryptoBotCoreMVC.Middlewares.IgnoreRouteMiddleware.Invoke(HttpContext context) in <Path>\CSharp\Projects\CryptoBotCoreMVC\CryptoBotCoreMVC\Middlewares\IgnoreRouteMiddleware.cs:line 23
   at Microsoft.AspNetCore.Diagnostics.DeveloperExceptionPageMiddleware.Invoke(HttpContext context)

HEADERS
=======
Accept: */*
Accept-Encoding: gzip, deflate, br
Accept-Language: pl-PL,pl;q=0.9,en-US;q=0.8,en;q=0.7
Cache-Control: no-cache
Connection: keep-alive
Content-Length: 244
Content-Type: multipart/form-data; boundary=----WebKitFormBoundary3RFkBWmZhhsbjzXr
Host: localhost
Pragma: no-cache
Referer: http://localhost/CryptoBotCoreMVC
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/77.0.3865.120 Safari/537.36
Sec-Fetch-Mode: cors
Origin: http://localhost
Sec-Fetch-Site: same-origin

现在我们有所进展,似乎在这种情况下更改 KestrelIIS 配置应该就足够了:

services.Configure<KestrelServerOptions>(options =>
{
    options.AllowSynchronousIO = true;
});
services.Configure<IISServerOptions>(options =>
{
    options.AllowSynchronousIO = true;
});

确实如此,我会尽快post详细解答。

为了使来自 IgniteUIIgUpload 控件与 ASP.NET CORE MVC 3.0 一起工作,您需要执行以下步骤(但请注意,它仍然不会分块传输大文件,为此请参阅 this 文章(ASP.NET CORE 尚未更新):

  1. 添加 Infragistics.Web.AspNetCoreIgniteUI NuGet 包或从您的 Infragistics 手动添加脚本、.css 文件和 .dll 文件作为依赖项的安装文件夹:

  2. web.config 文件中设置 maxAllowedContentLength:

  3. IIS中设置Maximum allowed content length (Bytes)CTRL + R > inetmgr > <Server or Website> > IIS Category > RequestFiltering > Actions Pane (to the right side) > Edit Feature Settings...

  4. Startup.cs 文件中包含 options.AllowSynchronousIO = true Kestrel and/or IIS 以防止 Middleware 抛出 Synchronous operations are disallowed. Call ReadAsync or set AllowSynchronousIO to true instead例外。

  5. maxFileSizeLimit 设置为正值,因为尽管有文档说明,如果将其设置为 null-1 或如果您根本不设置此选项。

  6. FileUploadPath 设置为应用程序驱动器(不是虚拟驱动器)上的物理目录。

  7. 渲染包含 ProgressUrlUploadUrlControlID 选项的控件,并将它们设置为下面可见的确切值,以防止 Middleware 抛出各种不明确的 404 错误(代码示例将在此答案的底部)。

  8. 得到想要的结果:

重要提示:IgnoreRoute 不是必需的!

代码示例:

Startup.cs:

using System.IO;
using Infragistics.Web.Mvc;
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Hosting;
using Microsoft.AspNetCore.Server.Kestrel.Core;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;

namespace IgUpload
{
    public class Startup
    {
        public void ConfigureServices(IServiceCollection services)
        {
            services.AddMvc(options => options.EnableEndpointRouting = false);
            services.Configure<KestrelServerOptions>(options => options.AllowSynchronousIO = true);
            services.Configure<IISServerOptions>(options => options.AllowSynchronousIO = true);
            services.Configure<UploadAppSettings>(options =>
            {
                options.maxFileSizeLimit = "1073741824"; // 1 GB
                options.FileUploadPath = $@"{Directory.GetCurrentDirectory()}\Data\UploadedFiles";
            });
        }

        public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
        {
            if (env.IsDevelopment()) app.UseDeveloperExceptionPage();
            app.UseStaticFiles();
            app.UseRouting();
            app.UseUploadModuleMiddleware();
            app.UseUploadHandlerMiddleware();
            app.UseMvc(routes => routes.MapRoute("default", "{controller=Home}/{action=Index}/{id?}"));
        }
    }
}

Index.cshtml:

@using Infragistics.Web.Mvc
@using Microsoft.AspNetCore.Http
@{
    ViewData["Title"] = "Index";
}

<div id="igFileUpload"></div>

<script src="~/libs/JQuery/jquery.js"></script>
<script src="~/libs/JQueryUI/jquery-ui.js"></script>
<script src="~/libs/infragistics/js/infragistics.core.js"></script>
<script src="~/libs/infragistics/js/infragistics.lob.js"></script>

@(
    Html.Infragistics().Upload()
        .ID("igFileUpload")
        .Mode(UploadMode.Multiple)
        .AutoStartUpload(true)
        .ProgressUrl(Url.Content("~/IGUploadStatusHandler.ashx"))
        .UploadUrl(Url.Content("~/ig_fua34sf345sdf13sdf3454erdsf2345asd3425df5235d54df345.aspx"))
        .ControlId("serverID1")
        .Width("600px")
        .Render()
)

<div id="error-message" style="color: #FF0000; font-weight: bold;"></div>

<script type="text/javascript">
    $(function () {
        $("#igFileUpload").bind({ iguploadonerror: function (e, args) {
                $("#error-message").html(args.errorMessage).stop(true, true).fadeIn(500).delay(3000).fadeOut(500);
            }
        });
    });
</script>