Search code examples
imageamazon-web-servicesamazon-s3vaporleaf

How to display image using AWS S3 and Vapor 3 and Leaf


I have set up AWS S3 bucket and I'm able to upload files there using Vapor 3 and Postman. I can also get files using Vapor 3 and Postman but I have a hard time to get and actually display files(png -images) on my browser (I'm using leaf).

So how can I display image files on the view? I'm new with HTML, AWS S3 and Vapor.

I'm using:

  • Vapor 3
  • AWS S3
  • Leaf package
  • S3 package
  • VaporExt package

I followed this tutorial to set up everything (except the get request and the bucket policies): https://fivedottwelve.com/blog/using-amazon-s3-with-vapor/

Here are my Vapor code:

/// GET reguest
func preparePresignedImageUrl(request: Request) throws -> String {

    let baseUrl = awsConfig.url
    let imagePath = awsConfig.imagePath
    let fileName = "x.png"


    guard var url = URL(string: baseUrl) else {
        throw Abort(.internalServerError)
    }
    url.appendPathComponent(imagePath)
    url.appendPathComponent(fileName)

    print("url is \(url)")
    let headers = ["x-amz-acl" : "public-read"]


    let s3 = try request.makeS3Signer()
    let result = try s3.presignedURL(for: .GET, url: url, expiration: Expiration.hour, headers: headers)
    /// Retrieve file data from S3

    guard let presignedUrl = result?.absoluteString else {
        throw Abort(.internalServerError)
    }

    return presignedUrl

}

The route:

// GET request
 group.get("aws", "image", use: preparePresignedImageUrl) 

And in the Postman when I make a GET request to that presignedURL it gives me a status code 200OK.

My showImage.leaf file:

#set("content") {
<h1>#(title)</h1>

// Here some html to get the image path and display the image?
  <img>

}

#embed("base")

Solution

  • So I am going to assume that you have a perfectly good URL to the image.

    Let's start by creating a route at GET /image:

    routes.get("image", use: image)
    
    func image(_ request: Request) -> EventLoopFuture<View> {
    
    }
    

    To properly render the Leaf view, we will need a context that contains the data for the Leaf variables. It will look something like this:

    struct ImageContext: Encodable {
        let title: String
        let imageURL: String
    }
    

    We also need to edit your leaf file so it will use the properties in the context:

    #set("content") {
    <h1>#(title)</h1>
    <img src="#(imageURL)">
    }
    
    #embed("base")
    

    Now we can render the leaf file with out new context and return the resulting view from the route handler. Here is what the implementation of the image route handler will look like:

    func image(_ request: Request) -> EventLoopFuture<View> {
        let context = ImageContext(title: "Image", imageURL: preparePresignedImageUrl(request: request)
        return try request.view().make("showImage", context)
    }
    

    Now you should be able to access localhost:8080/image from your browser and the image will be there!