In my experience, dynamically generating a file, serving it immediately, and not persisting it on the server is a pretty common use case. In general, this is one of two things – either a PDF download, or a CSV. While my Vapor tinkering hasn’t yet given me an opportunity to generate PDFs on the server, I have had an occasion to create a CSV, and wrote up a little helper for doing so.
import Vapor
struct TextFileResponse {
enum ResponseType {
case inline, attachment(filename: String)
}
var body: String
var type: ResponseType
var contentType: String
}
extension TextFileResponse: ResponseEncodable {
public func encodeResponse(for request: Request) -> EventLoopFuture<Response> {
var headers = HTTPHeaders()
headers.add(name: .contentType, value: contentType)
switch type {
case .inline:
headers.add(name: .contentDisposition, value: "inline")
case .attachment(let filename):
headers.add(name: .contentDisposition, value: "attachment; filename=\"\(filename)\"")
}
return request.eventLoop.makeSucceededFuture(.init(status: .ok, headers: headers, body: .init(string: body)))
}
}
That’ll work for any file you can assemble as text; CSV just struck me as being the most useful example. Use ResponseType.inline
for a file you want displayed in a browser tab, and .attachment
if it’s for downloading.
And if you’re doing a lot of CSVs, give yourself a nice little helper:
extension TextFileResponse {
static func csv(body: String, name: String) -> TextFileResponse {
.init(body: body, type: .attachment(filename: name), contentType: "text/csv")
}
}