【发布时间】:2020-10-23 05:25:31
【问题描述】:
=============== 更新 ===============
很抱歉,但在进行了所有的修改和更改、注释掉并重新输入,以尝试使其正常工作,我不小心停止了在 Create.cshtml 中调用脚本的表单。 View文件的形式实际上应该是这样的:
<form asp-controller="Documents" asp-action="Create" method="post" enctype="multipart/form-data" onSubmit="AJAXSubmit(this); return false;">
我还没有结束这个问题,因为Create.cshtml 中的脚本正在接收 400(错误请求)。这似乎是真正的问题 - 但是,我不明白为什么会这样。
如果我深入了解它,我会更新问题。希望这对某人来说是一个有用的答案。
=============== 结束更新 ===============
我一直关注https://docs.microsoft.com/en-us/aspnet/core/mvc/models/file-uploads?view=aspnetcore-3.1#upload-large-files-with-streaming 将大文件上传到网络应用程序。不幸的是,当我开始使用 MultipartReader (reader.ReadNextSectionAsync) 时,我收到了错误 unexpected end of stream, the content may have already been read by another component。
我已经下载了示例应用 (https://github.com/dotnet/AspNetCore.Docs/tree/master/aspnetcore/mvc/models/file-uploads/samples/3.x/SampleApp) 并且运行良好,但他们使用的是 Razor 页面,而我的项目不是。
一个显着的区别:当我从 github 运行演示应用程序时,当我单击 Upload 以上传大文件时,会立即调用 Controller 方法。但是,当我在我的应用程序上单击上传文档时,浏览器会显示正在上传的文件,然后调用 Controller 方法。
我是 ASP.NET 的新手,几天来我一直在用头撞砖墙,因此非常感谢任何帮助。
我的相关代码sn-ps如下:
GenerateAntiforgeryTokenCookieAttribute:将防伪令牌添加到 cookie,完全从示例文档中复制
DisableFormValueModelBindingAttribute:防止表单绑定的属性类,完全复制自示例文档
MultipartRequestHelper:完全从示例文档中复制
startup.cs:这也许是我的问题所在,因为我的项目没有使用 Razor 页面,而是使用功能文件夹,所以我偏离了示例。另外,我的项目正在使用功能文件夹。
public void ConfigureServices(IServiceCollection services)
{
...
services.AddMvc(o => o.Conventions.Add(new FeatureConvention()))
// first part is for feature folders
.AddRazorOptions(options =>
{
options.ViewLocationFormats.Clear();
options.ViewLocationFormats.Add("/Features/{3}/{1}/{0}.cshtml");
options.ViewLocationFormats.Add("/Features/{3}/{0}.cshtml");
options.ViewLocationFormats.Add("/Features/Shared/{0}.cshtml");
options.ViewLocationExpanders.Add(new FeatureViewLocationExpander());
})
// FeatureConvention and FeatureViewLocationExpander taken from https://docs.microsoft.com/en-us/archive/msdn-magazine/2016/september/asp-net-core-feature-slices-for-asp-net-core-mvc#feature-folders-in-aspnet-core-mvc
// next part is to prevent form value binding and to add the anti forgery token to the cookie
.AddRazorPagesOptions(options =>
{
options.Conventions.AddPageApplicationModelConvention(
"/Documents/Create",
model =>
{
model.Filters.Add(new GenerateAntiForgeryTokenCookieAttribute());
model.Filters.Add(new DisableFormValueModelBindingAttribute());
}
);
});
...
}
我的文件夹结构如下:
root
|
`-- Features
|
`-- Documents
|
`-- Create.cshtml
`-- DocumentsController.cs
创建.cshtml
@model DocumentCreateViewModel
<form asp-controller="Documents" asp-action="Create" method="post" enctype="multipart/form-data">
<div>
<label asp-for="filename"></label>
<input asp-for="filename""><input/>
</div>
<div>
<label asp-for="file"></label>
<input asp-for="file"><input/>
</div>
<div>
<button type="submit">submit</button>
</div>
</form>
@section Scripts {
<script>
"use strict";
async function AJAXSubmit (oFormElement) {
const formData = new FormData(oFormElement);
try {
const response = await fetch(oFormElement.action, {
method: 'POST',
headers: {
'RequestVerificationToken': getCookie('RequestVerificationToken')
},
body: formData
});
oFormElement.elements.namedItem("result").value =
'Result: ' + response.status + ' ' + response.statusText;
} catch (error) {
console.error('Error:', error);
}
}
function getCookie(name) {
var value = "; " + document.cookie;
var parts = value.split("; " + name + "=");
if (parts.length == 2) return parts.pop().split(";").shift();
}
</script>
}
DocumentCreateViewModel.cs
public class DocumentCreateViewModel
{
[Required]
public string Name { get; set; }
[Required]
public IFormFile File { get; set; }
}
DocumentsController.cs:是 UploadPhysical 的内容,完全从示例文档中复制而来,UploadPhysical 重命名为 Create
[HttpPost]
[DisableFormValueModelBinding]
[ValidateAntiForgeryToken]
public async Task<IActionResult> Create()
{
if (!MultipartRequestHelper.IsMultipartContentType(Request.ContentType))
{
ModelState.AddModelError("File",
$"The request couldn't be processed (Error 1).");
// Log error
return BadRequest(ModelState);
}
var boundary = MultipartRequestHelper.GetBoundary(
MediaTypeHeaderValue.Parse(Request.ContentType),
_defaultFormOptions.MultipartBoundaryLengthLimit);
var reader = new MultipartReader(boundary, HttpContext.Request.Body);
var section = await reader.ReadNextSectionAsync();
// ^^^^ this is where the "unexpected end of stream" exception is thrown
【问题讨论】:
-
根据您的查看代码,我发现您的 Create.cshtml 在发布表单数据时不使用 ajax。我建议您可以先尝试添加
onsubmit="AJAXSubmit(this);return false;",然后再试一次。如果这不能解决您的问题,我建议您可以尝试将您的项目上传到 github 让我们测试。注意:请删除所有个人信息,只留下代码让我们重现问题。 -
非常感谢您的回答 - 我已对问题添加了更新。我现在收到 400(错误请求),因此问题似乎出在表单发布(现在由 AJAX 完成)和控制器
DocumentsController之间。我会看看我是否能到达 400 的底部,但如果不能,我会按照你的建议去做并发布到 GitHub。
标签: c# asp.net asp.net-mvc asp.net-core