需求:该接口采用 POST 请求,且支持多图上传。
那么第一步,我们需要配置 FileMiddleware, FileMiddleware允许从项目的 Public 文件夹向 client 提供资源。你可以在这里存放 css 或者位图图片等静态文件。
public func configure(_ app: Application) throws {
/// 配置文件上传最大尺寸
app.routes.defaultMaxBodySize = "10mb"
/// 配置 FileMiddleware 中间件
app.middleware.use(FileMiddleware(publicDirectory: app.directory.publicDirectory))
....
}
然后我们配置一个上传路由,实现上传功能,我们将逻辑实现的代码放到 FileController.swift 中:
//routes.swift
func routes(_ app: Application) throws {
//
try app.register(collection: FileController())
...
}
在 FileController.swift 中:
import Fluent
import Vapor
struct FileController: RouteCollection {
func boot(routes: RoutesBuilder) throws {
let file = routes.grouped("file")
file.post("upload", use: uploadImage)
}
}
extension FileController {
private func uploadImage(_ req: Request) async throws -> OutJson<[String]> {
let inUpload = try req.content.decode(InUpload.self)
let formatter = DateFormatter()
formatter.dateFormat = "y-m-d-HH-MM-SS-"
let result = try await withThrowingTaskGroup(of: String.self, returning: [String].self, body: { (group) async throws in
for file in inUpload.files {
group.addTask {
let prefix = formatter.string(from: .init())
let parentDir = "\(inUpload.dir)/"
let dir = req.application.directory.publicDirectory + parentDir
let fileName = prefix + file.filename
let path = dir + fileName
// 创建目录
try FileManager.default.createDirectory(atPath: dir, withIntermediateDirectories: true, attributes: nil)
try await req.fileio.writeFile(file.data, at: path)
return parentDir + fileName
}
}
var images = [String]()
for try await img in group {
images.append(img)
}
return images
})
return OutJson(success: result)
}
}
FileController 的主要逻辑就是:
- 获取到接口参数
- 遍历参数中的
files: [File],拼接好参数,进行保存到指定位置中 - 让图片路径返回给请求端
对于接口参数的解析:
let inUpload = try req.content.decode(InUpload.self)
InUpload.swift 也非常简单:
import Vapor
struct InUpload: In {
var files: [File]
var type: Int // 表情 = 0,壁纸 = 1,图片 = 2, 文件=3
// 根据 type 保存到不同的文件夹中
var dir: String {
if type == 0 {
return "emotions"
}
else if type == 1 {
return "wallpaper"
}
else if type == 2 {
return "images"
}
else if type == 3 {
return "files"
} else {
return "other"
}
}
}
图片的拼接逻辑,根据type的不同,保存到不同的目录中。
在保存图片的时候,我们使用了 Swift 的结构化并发 withThrowingTaskGroup, 具体使用可以看上面的代码。
添加完成上面代码,我们可以通过 Postman 测试:
多图上传的时候,接收的 files 是个数组,我们可以将 Key 设置为 files[下标] 方式,Value 类型选择文件即可。
通过上面的简单说明,相信你也可以非常容易的实现文件上传接口了。
如果想阅读更多文章,不妨关注 OldBirds 微信公众号。