Search code examples
swiftvaporserver-side-swiftswift-nio

Running actions after file stream in Vapor 4


I run a website that handles large files (uploads and downloads). Currently I am transitioning the site from Perfect to Vapor. In Perfect, it is relatively simple to stream a file to users, and then perform some actions AFTER the file is done.

I am trying to do the same thing in Vapor, but I can't seem to figure out how to set a callback when the stream finished, and when it finished, whether it was completely downloaded by the user or there was an interruption.

Is anyone aware of how to do this? Here are some things that I have tried.

This is the basic structure

func downloadFile(request: Request) -> EventLoopFuture<Response> {
    //do some authentication stuff here
    
    let promise = request.eventLoop.makePromise(of: Response.self)
    let response = request.fileio.streamFile(at: "somePath")
    promise.succeed(response)
    let future = promise.futureResult
    return future
}

First modification

func downloadFile(request: Request) -> EventLoopFuture<Response> {
    //do some authentication stuff here
    
    let promise = request.eventLoop.makePromise(of: Response.self)
    let response = request.fileio.streamFile(at: "somePath")
    promise.succeed(response)
    let future = promise.futureResult
    
    future.eventLoop.next().execute {
        //run some post-download processing here.
        //ideally I would like to know if the download completed or not.
    }
    
    return future
}

Second modification

func downloadFile(request: Request) -> EventLoopFuture<Response> {
    //do some authentication stuff here
    
    let promise = request.eventLoop.makePromise(of: Response.self)
    let response = request.fileio.streamFile(at: "somePath")
    promise.succeed(response)
    let future = promise.futureResult
    
    future.whenComplete { result in
        switch result {
            case .succeed:
            //run succeed processing here.
            case .failure:
            //run failure code here
        }
    }
    
    return future
}

In all the above cases, what happens is all the processing is done immediately (likely because we're dealing with futures and promises and everything is async).

My question is, is there any way in Vapor to know when a large file has finished streaming and what the result of the stream is? Some middleware maybe?


Solution

  • As of a recent release of Vapor, there is now an optional handler that you can use to preform actions on success / failure of download.

    let response = request.fileio.streamFile(at: somepath) { result in
        switch result {
            case .success(): //do success processing
            case .failure(let error): // do failure processing and can print(error)
        }
    }