Marc
Marc

Reputation: 3070

How to respond with an image using Vapor?

I just want to have a controller action that is basically doing the same as accessing an image directly through the Public/ folder. The difference is that the route can be whatever you want and the returned image will be the one that is determined inside of the controller function. But how to create a proper response?

My approach looks like this:

import Vapor

final class ImageController {
    func read(_ req: Request) throws -> Future<Data> {
        let directory = DirectoryConfig.detect()
        let promise = req.eventLoop.newPromise(Data.self)
        req.eventLoop.execute {
            do {
                let data = try Data(contentsOf: URL(fileURLWithPath: directory.workDir)
                    .appendingPathComponent("Public", isDirectory: true)
                    .appendingPathComponent("image.png"))
                promise.succeed(result: data)
            } catch {
                promise.fail(error: error)
            }
        }
        return promise.futureResult
//        try req.content.encode(data, as: .png)
    }
}

But it seems to me I'm overcomplicating it, do I?

Upvotes: 4

Views: 2153

Answers (2)

marc-medley
marc-medley

Reputation: 9832

But how to create a proper response?

Directly create a Response and set the MediaType: request.makeResponse(data, as: MediaType.png)

Example:

struct ImageController: RouteCollection {
    func boot(router: Router) throws {
        let imageRoutes = router.grouped("images")
        // GET /images/get-test-image
        imageRoutes.get("get-test-image", use: getTestImage)    
    }

    func getTestImage(_ request: Request) throws -> Response {
        // add controller code here 
        // to determine which image is returned
        let filePath = "/path/to/image18.png"
        let fileUrl = URL(fileURLWithPath: filePath)

        do {
            let data = try Data(contentsOf: fileUrl)
            // makeResponse(body: LosslessHTTPBodyRepresentable, as: MediaType)
            let response: Response = request.makeResponse(data, as: MediaType.png)
            return response
        } catch {
            let response: Response = request.makeResponse("image not available")
            return response
        }
    }
}

Upvotes: 10

Nick
Nick

Reputation: 5180

Two additional approaches:

  1. A route using Leaf to deploy the image:

    route.get("/my/random/route") {
         request -> Future<View> in
         return try request.view().render("image", ["imgname":"image.png"])
    }
    

With image.leaf containing:

<img src="#(imgname)">
  1. A re-direct to the original location in Public:

    route.get("/my/random/route") {
        request -> Response in
        return request.redirect(to: "/image.png")
    }
    

Upvotes: 5

Related Questions