.NET Core 6下C#大文件上传Web API转F#报错求解
.NET Core 6 大文件上传Web API F# 正确实现
核心语法修正点
转换过程中两类高频编译错误对应F#语法规则如下:
- 所有C#中带
out参数的TryParse类方法,F#会自动封装为返回**(解析成功布尔值, 解析结果对象)**的元组,不需要提前声明out变量传入,直接解构元组即可拿到结果 - F#的
task{}计算表达式中,用let!绑定异步返回值、do!执行无返回值的异步操作,等价于C#的await关键字,不要直接套用C#的await写法 - 循环读取流程中需要反复修改的变量用
mutable关键字声明,赋值使用<-操作符 - 控制器返回不同类型的响应结果时,需要显式向上转型为
IActionResult统一类型,否则会触发类型不匹配编译错误
完整可运行F#代码
代码完全对齐原C#实现的逻辑:无参动作、跳过默认模型绑定、直接读取请求体、用MultipartReader分块解析请求、流式读写不加载全量文件到内存,支持超大文件上传:
open Microsoft.AspNetCore.Mvc open Microsoft.AspNetCore.WebUtilities open Microsoft.Net.Http.Headers open System open System.IO open System.Threading.Tasks [<ApiController>] [<Route("api/[controller]")>] // 关闭默认请求大小限制,适配大文件上传 [<DisableRequestSizeLimit>] [<RequestFormLimits(MultipartBodyLengthLimit = Int64.MaxValue, ValueLengthLimit = Int32.MaxValue)>] type FileUploadController() = inherit ControllerBase() [<HttpPost("upload-large-file")>] member this.LargeFileUpload() : Task<IActionResult> = task { // 校验请求类型合法性 let parseContentTypeRes, parsedContentType = MediaTypeHeaderValue.TryParse(this.Request.ContentType) if not parseContentTypeRes || not parsedContentType.MediaType.Equals("multipart/form-data", StringComparison.OrdinalIgnoreCase) then return this.BadRequest("仅支持multipart/form-data类型请求") :> IActionResult // 读取multipart请求边界 let boundary = HeaderUtilities.RemoveQuotes(parsedContentType.Boundary).Value if String.IsNullOrWhiteSpace(boundary) then return this.BadRequest("请求缺少合法的multipart边界参数") :> IActionResult // 初始化分块读取器,直接绑定原始请求流,跳过默认模型绑定 use multipartReader = new MultipartReader(boundary, this.Request.Body) // 自定义文件存储目录,可根据业务调整 let saveDir = Path.Combine(Directory.GetCurrentDirectory(), "UploadFiles") Directory.CreateDirectory(saveDir) |> ignore let mutable successCount = 0 let mutable currentSection = Unchecked.defaultof<MultipartSection> // 循环读取每个分块 let! nextSection = multipartReader.ReadNextSectionAsync() currentSection <- nextSection while currentSection <> null do let parseDispRes, contentDisp = ContentDispositionHeaderValue.TryParse(currentSection.ContentDisposition) // 仅处理文件类型分块,忽略普通表单字段 if parseDispRes && contentDisp.IsFileDisposition() then let fileName = HeaderUtilities.RemoveQuotes(contentDisp.FileName.Value).Value let targetFile = Path.Combine(saveDir, Path.GetFileName(fileName)) // 流式写入文件,缓冲区大小设为1MB,可自行调整 do! use fileStream = File.Create(targetFile) task { let buffer = Array.zeroCreate<byte> (1024 * 1024) let mutable readBytes = 0 let! firstRead = currentSection.Body.ReadAsync(buffer, 0, buffer.Length) readBytes <- firstRead while readBytes > 0 do do! fileStream.WriteAsync(buffer, 0, readBytes) let! nextRead = currentSection.Body.ReadAsync(buffer, 0, buffer.Length) readBytes <- nextRead } :> Task successCount <- successCount + 1 // 读取下一个分块 let! next = multipartReader.ReadNextSectionAsync() currentSection <- next return this.Ok({| SuccessUploadCount = successCount; Msg = "上传完成" |}) :> IActionResult }
部署注意事项
如果你使用IIS、Nginx等反向代理托管服务,需要同步修改代理层的请求体大小限制,否则请求会在到达接口前被代理拦截。比如IIS需要在web.config中配置
requestLimits节点的maxAllowedContentLength为你需要的阈值,Nginx需要调整client_max_body_size配置。
内容的提问来源于stack exchange,提问作者Ankur Shah
相关产品推荐
相关产品推荐

