Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Proper way of responding to a request with a temp file in Vapor

I have a Vapor server running and it's creating files. Those files are temporary files, so they should not stay on the server.

Currently I use the following:

let data = try Data(contentsOf: tempURL)
try FileManager.default.removeItem(at: tempURL)
let response = Response(status: .ok, headers: HTTPHeaders(headers), body: Response.Body(data: data))

So I read the contents of the file, delete that file (as it's temporary only) and then respond with it.

The problem that I now see is that sometimes, very hard to reproduce, the contents of that file is only submitted partially. I don't know exactly what the problem is yet, but I did some digging and at least found a Radar (https://openradar.appspot.com/39621032), saying that this function isn't working like it should on Linux (which I use.. I use it in a Docker-Container)

So just to make sure this is not the problem, I'd like to know the Vapor way of responding with a file and then deleting it afterwards?!?

like image 582
Georg Avatar asked Oct 30 '25 22:10

Georg


1 Answers

If this question is still actual, I want to suggest my solution. My system is Vapor 4.49.0, Swift 5.5, macOS 11.6 (should work on Ubuntu 20.04). I will post here small but full file routes.swift, please, look:

//
//  routes.swift
//
//
//  Created by Alex on 09.10.2021.
//

import Vapor

func routes(_ app: Application) throws {
    
    app.get(":fileName") { req -> EventLoopFuture<Response> in
        let badResponse = Response(status: .forbidden, version: .http1_0, headers: HTTPHeaders(), body: .empty)
        
        guard let fileName = req.parameters.get("fileName") else {
            return req.eventLoop.future(badResponse)
        }
        
        let filePath = app.directory.workingDirectory + fileName
        
        guard let file = File(filePath: filePath) else {
            return req.eventLoop.future(badResponse)
        }
        
        return req.fileio.collectFile(at: filePath).map { biteBuffer in
            let body = Response.Body(buffer: biteBuffer)
            
            let response = Response(status: .ok, headers: HTTPHeaders(), body: body)
            
            file.delete()
            
            return response
        }
    }
    
}

final class File {
    
    private let fileManager = FileManager.default
    private let url: URL
    
    init?(filePath: String) {
        guard let url = URL(string: "file:" + filePath) else {
            return nil
        }
        
        self.url = url
    }
    
    func delete() {
        do {
            try fileManager.removeItem(at: url)
        } catch {
        }
    }
    
}

I want to hear your reply. If you can test this code, I will be happy. Thanks.

like image 161
Alex Avatar answered Nov 02 '25 12:11

Alex



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!