Thursday, September 11, 2025
HomeiOS DevelopmentFile add API server in Vapor 4

File add API server in Vapor 4


A easy file add server written in Swift

For this straightforward file add tutorial we’ll solely use the Vapor Swift package deal as a dependency. 📦


import PackageDescription

let package deal = Package deal(
    title: "myProject",
    platforms: [
       .macOS(.v10_15)
    ],
    dependencies: [
        .package(url: "https://github.com/vapor/vapor", from: "4.35.0"),
    ],
    targets: [
        .target(
            name: "App",
            dependencies: [
                .product(name: "Vapor", package: "vapor"),
            ],
            swiftSettings: [
                .unsafeFlags(["-cross-module-optimization"], .when(configuration: .launch))
            ]
        ),
        .goal(title: "Run", dependencies: [.target(name: "App")]),
        .testTarget(title: "AppTests", dependencies: [
            .target(name: "App"),
            .product(name: "XCTVapor", package: "vapor"),
        ])
    ]
)

You may setup the venture with the required recordsdata utilizing the Vapor toolbox, alternatively you possibly can create the whole lot by hand utilizing the Swift Package deal Supervisor, lengthy story brief, we simply want a starter Vapor venture with out extra dependencies. Now in the event you open the Package deal.swift file utilizing Xcode, we are able to setup our routes by altering the configure.swift file.

import Vapor

public func configure(_ app: Software) throws {

    
    app.middleware.use(FileMiddleware(publicDirectory: app.listing.publicDirectory))

    
    app.routes.defaultMaxBodySize = "10mb"

    
    app.publish("add") { req -> EventLoopFuture<String> in
        let key = strive req.question.get(String.self, at: "key")
        let path = req.software.listing.publicDirectory + key
        return req.physique.acquire()
            .unwrap(or: Abort(.noContent))
            .flatMap { req.fileio.writeFile($0, at: path) }
            .map { key }
    }
}

First we use the FileMiddleware, it will permit us to server recordsdata utilizing the Public listing inside our venture folder. If you do not have a listing named Public, please create one, for the reason that file add server will want that. Remember to offer correct file system permissions if mandatory, in any other case we cannot have the ability to write our information contained in the listing. 📁

The following factor that we set is the default most physique measurement. This property can restrict the quantity of information that our server can settle for, you do not actually need to use this technique for giant recordsdata as a result of uploaded recordsdata will likely be saved within the system reminiscence earlier than we write them to the disk.

If you wish to add giant recordsdata to the server you must think about streaming the file as a substitute of amassing the file information from the HTTP physique. The streaming setup would require a bit extra work, nevertheless it’s not that difficult, if you’re fascinated by that resolution, you must learn the Information API and the physique streaming part utilizing official Vapor docs web site.

This time we simply desire a useless easy file add API endpoint, that collects the incoming information utilizing the HTTP physique right into a byte buffer object, then we merely write this buffer utilizing the fileio to the disk, utilizing the given key from the URL question parameters. If the whole lot was accomplished with out errors, we are able to return the important thing for the uploaded file.

File add duties utilizing the URLSession API The Basis frameworks provides us a pleasant API layer for widespread networking duties. We are able to use the URLSession uploadTask technique to ship a brand new URLRequest with a knowledge object to a given server, however IMHO this API is sort of unusual, as a result of the URLRequest object already has a httpBody property, however you need to explicitly move a “from: Information?” argument if you assemble the duty. However why? 🤔

import Basis

extension URLSession {

    func uploadTask(with request: URLRequest, completionHandler: @escaping (Information?, URLResponse?, Error?) -> Void) -> URLSessionUploadTask {
        uploadTask(with: request, from: request.httpBody, completionHandler: completionHandler)
    }
}

Anyway, I made just a little extension technique, so after I create the URLRequest I can set the httpBody property of it and safely move it earlier than the completion block and use the contents because the from parameter. Very unusual API design alternative from Apple… 🤐

We are able to put this little snippet right into a easy executable Swift package deal (or after all we are able to create a whole software) to check our add server. In our case I will place the whole lot right into a major.swift file.

import Basis
import Dispatch

extension URLSession {

    func uploadTask(with request: URLRequest, completionHandler: @escaping (Information?, URLResponse?, Error?) -> Void) -> URLSessionUploadTask {
        uploadTask(with: request, from: request.httpBody, completionHandler: completionHandler)
    }
}


let fileData = strive Information(contentsOf: URL(fileURLWithPath: "/Customers/[user]]/[file].png"))
var request = URLRequest(url: URL(string: "http://localhost:8080/add?key=(UUID().uuidString).png")!)
request.httpMethod = "POST"
request.httpBody = fileData

let activity = URLSession.shared.uploadTask(with: request) { information, response, error in
    guard error == nil else {
        fatalError(error!.localizedDescription)
    }
    guard let response = response as? HTTPURLResponse else {
        fatalError("Invalid response")
    }
    guard response.statusCode == 200 else {
        fatalError("HTTP standing error: (response.statusCode)")
    }
    guard let information = information, let end result = String(information: information, encoding: .utf8) else {
        fatalError("Invalid or lacking HTTP information")
    }
    print(end result)
    exit(0)
}

activity.resume()
dispatchMain()

The above instance makes use of the Dispatch framework to attend till the asynchronous file add finishes. It is best to change the situation (and the extension) of the file if mandatory earlier than you run this script. Since we outlined the add route as a POST endpoint, we have now to set the httpMethod property to match this, additionally we retailer the file information within the httpBody variable earlier than we create our activity. The add URL ought to comprise a key, that the server can use as a reputation for the file. You may add extra properties after all or use header values to test if the consumer has correct authorization to carry out the add operation. Then we name the add activity extension technique on the shared URLSession property. The good factor about uploadTask is that you would be able to run them on the background if wanted, that is fairly helpful if it involves iOS growth. 📱

Contained in the completion handler we have now to test for just a few issues. Initially if there was an error, the add should have failed, so we name the fatalError technique to interrupt execution. If the response was not a sound HTTP response, or the standing code was not okay (200) we additionally cease. Lastly we need to retrieve the important thing from the response physique so we test the information object and convert it to a UTF8 string if attainable. Now we are able to use the important thing mixed with the area of the server to entry the uploaded file, this time I simply printed out the end result, however hey, that is only a demo, in an actual world software you would possibly need to return a JSON response with extra information. 😅

Vanilla JavaScript file uploader

Yet another factor… you need to use Leaf and a few Vanilla JavaScript to add recordsdata utilizing the newly created add endpoint. Truly it is very easy to implement a brand new endpoint and render a Leaf template that does the magic. You may want some fundamental HTML and some strains of JS code to submit the contents of the file as an array buffer. This can be a fundamental instance.



  
    
    
    File add
  
  
      
      

As you possibly can see it is an ordinary XHR request mixed with the FileReader JavaScript API. We use the FileReader to transform our enter to a binary information, this manner our server can write it to the file system within the anticipated format. Usually persons are utilizing a multipart-encoded kind to entry recordsdata on the server, however when you need to work with an API you can too switch uncooked file information. If you wish to study extra about XHR requests and AJAX calls, you must learn my earlier article.

I even have a publish about totally different file add strategies utilizing commonplace HTML kinds and a Vapor 4 server as a backend. I hope you may discover the precise resolution that you just want to your software. 👍

RELATED ARTICLES

LEAVE A REPLY

Please enter your comment!
Please enter your name here

Most Popular

Recent Comments