Vapor 4+ 无内置 File 类型,文件上传需通过 HTTPPart 的 filename 判断是否为文件,并用 part.stream() 流式处理避免内存溢出,而非 part.data() 全量加载。

Swift Vapor 中 File 对象不存在,实际用的是 FileIO 和 HTTPPart
Vapor 4+ 没有名为 File 的内置类型——这是常见误解。上传文件时,请求体被解析为 multipart 数据,每个字段对应一个 HTTPPart;而文件内容的读写依赖 FileIO(异步 I/O 工具)或直接流式处理。若你在文档或旧代码里看到 File,大概率是自定义模型或 Vapor 2/3 的遗留命名。
如何从 HTTPPart 提取上传的文件名和内容
multipart 请求中,文件字段会带 Content-Disposition 头,其中包含 filename 参数。Vapor 自动解析它,但需手动检查是否为文件(而非普通表单字段):
app.post("upload") { req -> EventLoopFuture in guard let part = req.http.body.part(named: "file") else { return req.eventLoop.makeFailedFuture(Abort(.badRequest)) } // 判断是否为文件:检查是否有 filename guard let filename = part.filename else { return req.eventLoop.makeFailedFuture(Abort(.badRequest, reason: "No filename provided")) } // 可选:校验文件类型(基于 Content-Type) let contentType = part.contentType?.description ?? "application/octet-stream" // 读取全部内容到内存(仅限小文件!) return part.data() .flatMap { data in let outputPath = "/tmp/uploaded_(filename)" return req.fileio.writeFile(data, at: outputPath) } .map { _ in .ok } }
-
part.filename是可选值,只有带filename=...的 part 才算文件上传 -
part.data()把整个文件加载进内存,不适用于大文件;生产环境应改用part.stream()+ 分块写入 -
req.fileio.writeFile(...)是异步写入,路径需确保目录存在且进程有写权限
大文件上传必须用 part.stream() 避免内存爆炸
上传 10MB+ 文件时,part.data() 会把全部字节载入 RAM,极易触发 OOM。正确做法是用 part.stream() 获取 AsyncThrowingStream,逐块写入磁盘或转发:
return part.stream() .write(to: "/tmp/(filename)", on: req.application.fileio) .map { _ in .ok }
-
AsyncThrowingStream是 swift 5.5+ 原生流类型,Vapor 封装了底层 byte buffer 流控 -
write(to:on:)是AsyncThrowingStream的扩展方法,自动处理分块、flush 和 close - 若需校验哈希或转存到 S3,应在 stream 的
foreach中处理每个ByteBuffer,而不是先 collect 全量
常见错误:part.filename 为 nil 却当成文件处理
最常踩的坑是没区分表单字段和文件字段。例如 html 写成 (无 type="file"),或漏了 enctype="multipart/form-data",此时 part.filename == nil,但开发者仍调用 part.data() —— 这会静默读取空内容,后续保存得到零字节文件。
- 永远先判
if let filename = part.filename,再做文件逻辑 - 前端必须用
,且文件 input 要有
name与后端part(named: "...")一致 - Vapor 日志不会报错,但
part.contentType在非文件场景下通常为nil或text/plain,可辅助判断
文件上传不是“拿个 File 对象点保存”那么简单;核心在于理解 multipart 解析时机、流式边界控制,以及 filename 字段才是文件身份的唯一可信标识。