Serving ‘files’ in Vapor

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))) } }
Code language: Swift (swift)

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") } }
Code language: Swift (swift)

Leave a Reply

Your email address will not be published. Required fields are marked *

This site uses Akismet to reduce spam. Learn how your comment data is processed.