Skip to content

Instantly share code, notes, and snippets.

@bre7
Created April 6, 2018 03:12
Show Gist options
  • Save bre7/29e5080c195d8ce85d54a7eb29ffc8a7 to your computer and use it in GitHub Desktop.
Save bre7/29e5080c195d8ce85d54a7eb29ffc8a7 to your computer and use it in GitHub Desktop.
Vapor 3 Middleware to serve files from the public folder and respect Cache-Control headers
import Vapor
/// Services files from the public folder.
public final class FileMiddlewareWithCache: Middleware, Service {
/// The public directory.
/// note: does _not_ end with a slash
let publicDirectory: String
public var webTypes = [MediaType]()
enum CacheControl: String {
case css, js
case svg, jpg, jpeg, gif, png, webp
var maxAge: UInt {
switch self {
case .css, .js: return 31_536_000 // 1 year (must use versioning)
case .svg, .jpg, .jpeg, .gif, .png, .webp: return // 1 year (must use versioning)
}
}
}
/// Creates a new filemiddleware.
public init(publicDirectory: String) {
self.publicDirectory = publicDirectory.hasSuffix("/") ? publicDirectory : publicDirectory + "/"
}
/// See Middleware.respond.
public func respond(to req: Request, chainingTo next: Responder) throws -> Future<Response> {
var path = req.http.url.path
if path.hasPrefix("/") {
path = String(path.dropFirst())
}
guard !path.contains("../") else {
throw Abort(.forbidden)
}
let filePath = self.publicDirectory + path
var isDir: ObjCBool = false
guard FileManager.default.fileExists(atPath: filePath, isDirectory: &isDir) else {
return try next.respond(to: req)
}
guard !isDir.boolValue else {
return try next.respond(to: req)
}
if let ext = filePath.components(separatedBy: ".").last,
let cacheControlForExt = CacheControl(rawValue: ext) {
return try req.streamFile(at: filePath)
.map(to: Response.self) { res in
res.http.headers.replaceOrAdd(name: HTTPHeaderName.cacheControl, value: "max-age=\(cacheControlForExt.maxAge)")
return res
}
} else {
return try req.streamFile(at: filePath)
}
}
}
@derekli66
Copy link

Hi, I am Derek. I am new to Vapor 3. I have two questions about using FileMiddleware.

  1. Can I create a directory in Pubic folder when receiving a POST request through FileMiddleware?

  2. Is there a way to get the Public folder path when Vapor app is running? Because I expected I could use this path and FileManager to create directory in Public folder.

Please excuse me to bother you with above questions. I came across this gist post when I was searching the way to create directory in Public folder. Hope there is a hint for me to achieve that.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment