c# - 上传完成前文件上传未命中控制器并临时保存到磁盘
问题描述
假设我在具有 1GB 存储和 1GB RAM 的设备上运行 .NET Core API。我想将文件从我的网站直接上传到 FTP 服务器,而不会将文件缓存在内存或磁盘中。我有这个用于下载文件的工作,因为我基本上充当代理,通过在流中打开 FTP 文件,然后将其直接流式传输到HttpContext.Request.Body
.
对于上传,我想立即点击控制器。我现在可以看到它缓存到磁盘,这可能是因为我的EnableBuffering
属性。我有一个<form method="post" enctype="multipart/form-data">
POST 到我的 .NET Core 后端的常规程序。控制器如下所示:
[EnableBuffering]
[RequestFormLimits(ValueLengthLimit = int.MaxValue, MultipartBodyLengthLimit = long.MaxValue)]
[HttpPost("[action]")]
public async Task<IActionResult> Upload(string path)
{
if (!IsMultipartContentType(HttpContext.Request.ContentType))
return BadRequest("Not multipart request");
await _fileProvider.Upload(path);
return Ok();
}
启用缓冲:
public class EnableBufferingAttribute : Attribute, IResourceFilter
{
public void OnResourceExecuting(ResourceExecutingContext context)
{
context.HttpContext.Request.EnableBuffering();
}
public void OnResourceExecuted(ResourceExecutedContext context) {}
}
上传逻辑:
public async Task Upload(string path)
{
const int buffer = 8 * 1024;
var request = _httpContextAccessor.HttpContext.Request;
var boundary = request.GetMultipartBoundary();
var reader = new MultipartReader(boundary, request.Body, buffer);
var section = await reader.ReadNextSectionAsync();
using (var client = await _ftpHelper.GetFtpClient())
{
while (section != null)
{
var fileSection = section.AsFileSection();
if (fileSection != null)
{
var fileName = fileSection.FileName;
var uploadPath = Path.Combine(path, fileName);
var stream = await client.OpenWriteAsync(uploadPath);
await section.Body.CopyToAsync(stream, buffer);
}
section = await reader.ReadNextSectionAsync();
}
}
}
如果我不启用缓冲,我会得到:
System.IO.IOException:Stream 意外结束,内容可能已被另一个组件读取。
但是,通过启用缓冲,我可以看到它说:
确保 requestBody 可以被多次读取。通常在内存中缓冲请求体;将大于 30K 字节的请求写入磁盘。
所以这部分是有道理的。但是,我该如何解决这个问题?如果我在方法的第一行设置断点Upload()
,然后开始上传,它会先上传文件,然后点击断点。
我可以解决这个问题,让它立即访问我的控制器并开始上传到 FTP 服务器,而不先保存到磁盘吗?
解决方案
Upload
在进入方法之前处理请求的原因是表单模型绑定。
Upload
方法有path
参数,它的值来自表单数据。因此,需要在调用控制器方法之前解析请求以path
从表单数据中提取参数值。同样在这种情况下,上传文件的列表和内容可以通过类的属性使用Files
可用的集合来检索。HttpContext
ControllerBase
this.HttpContext.Request.Form.Files
解决此问题的最简单方法是禁用表单模型绑定并通过path
查询字符串传递参数。
[HttpPost("[action]")]
[DisableFormValueModelBinding]
public async Task<IActionResult> Upload([FromQuery] string path)
的源代码DisableFormValueModelBindingAttribute
可以在 ASP.NET Core 中的上传文件一文中找到。
[AttributeUsage(AttributeTargets.Class | AttributeTargets.Method)]
public class DisableFormValueModelBindingAttribute : Attribute, IResourceFilter
{
public void OnResourceExecuting(ResourceExecutingContext context)
{
var factories = context.ValueProviderFactories;
factories.RemoveType<FormValueProviderFactory>();
factories.RemoveType<FormFileValueProviderFactory>();
factories.RemoveType<JQueryFormValueProviderFactory>();
}
public void OnResourceExecuted(ResourceExecutedContext context)
{
}
}
推荐阅读
- java - 为 Android 应用程序制作动画背景不起作用
- ios - 警报接受时的 SwiftUI 刷新列表
- c - 对(readline、pthread_create、pthread_detach)的未定义引用,makefile 不包括库
- c# - FontAwesome / Xamarin - 设置字形不能从后面的代码中工作
- javascript - 在 redux reducer 中更新状态的更好方法
- sockets - 使用 cookie 授权带有 elixir 和苦艾酒的 graphql 订阅
- ios - 重复 iOS 通知
- python - Python model.fit_generator 卡在第一个 epoch 并尝试计算“未知”步数
- css - 造型材料表(特别是高度)
- java - 无法使用 Java 进程执行带有包含 # (哈希)字符的参数的 python 可执行文件