10.1 C
Canberra
Friday, September 20, 2024

File add API server in Vapor 4


Learn to construct a quite simple file add API server utilizing Vapor 4 and URLSession add process on the shopper facet.

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. 📦

// swift-tools-version:5.3
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’ll be able to setup the venture with the required recordsdata utilizing the Vapor toolbox, alternatively you possibly can create every part by hand utilizing the Swift Package deal Supervisor, lengthy story brief, we simply want a starter Vapor venture with out further dependencies. Now in case you open the Package deal.swift file utilizing Xcode, we will setup our routes by altering the configure.swift file.

import Vapor

public func configure(_ app: Utility) throws {

    /// allow file middleware
    app.middleware.use(FileMiddleware(publicDirectory: app.listing.publicDirectory))

    /// set max physique dimension
    app.routes.defaultMaxBodySize = "10mb"

    /// setup the add handler
    app.publish("add") { req -> EventLoopFuture in
        let key = attempt req.question.get(String.self, at: "key")
        let path = req.software.listing.publicDirectory + key
        return req.physique.gather()
            .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. Should you don’t have a listing named Public, please create one, for the reason that file add server will want that. Don’t overlook to provide correct file system permissions if vital, in any other case we received’t be capable to write our knowledge contained in the listing. 📁

The following factor that we set is the default most physique dimension. This property can restrict the quantity of information that our server can settle for, you don’t actually need to use this methodology for big recordsdata as a result of uploaded recordsdata might be saved within the system reminiscence earlier than we write them to the disk.

If you wish to add giant recordsdata to the server it is best to take into account streaming the file as a substitute of amassing the file knowledge from the HTTP physique. The streaming setup would require a bit extra work, but it surely’s not that sophisticated, if you’re fascinated by that answer, it is best to learn the Recordsdata API and the physique streaming part utilizing official Vapor docs web site.

This time we simply desire a lifeless easy file add API endpoint, that collects the incoming knowledge 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 every part was accomplished with out errors, we will 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 will use the URLSession uploadTask methodology to ship a brand new URLRequest with an information object to a given server, however IMHO this API is kind of unusual, as a result of the URLRequest object already has a httpBody property, however it’s important to explicitly go a “from: Knowledge?” argument while you assemble the duty. However why? 🤔

import Basis

extension URLSession {

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

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

We will put this little snippet right into a easy executable Swift package deal (or after all we will create a complete software) to check our add server. In our case I’ll place every part right into a primary.swift file.

import Basis
import Dispatch

extension URLSession {

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


let fileData = attempt Knowledge(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 process = URLSession.shared.uploadTask(with: request) { knowledge, 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 knowledge = knowledge, let consequence = String(knowledge: knowledge, encoding: .utf8) else {
        fatalError("Invalid or lacking HTTP knowledge")
    }
    print(consequence)
    exit(0)
}

process.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 placement (and the extension) of the file if vital earlier than you run this script. Since we outlined the add route as a POST endpoint, we’ve to set the httpMethod property to match this, additionally we retailer the file knowledge within the httpBody variable earlier than we create our process. The add URL ought to comprise a key, that the server can use as a reputation for the file. You’ll be able to 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 process extension methodology on the shared URLSession property. The good factor about uploadTask is which you can run them on the background if wanted, that is fairly useful if it involves iOS improvement. 📱

Contained in the completion handler we’ve to test for a couple of issues. To start with if there was an error, the add should have failed, so we name the fatalError methodology to interrupt execution. If the response was not a legitimate 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 info object and convert it to a UTF8 string if attainable. Now we will use the important thing mixed with the area of the server to entry the uploaded file, this time I simply printed out the consequence, however hey, that is only a demo, in an actual world software you would possibly need to return a JSON response with further knowledge. 😅

Vanilla JavaScript file uploader

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



  
    
    
    File add
  
  
      
      
File add API server in Vapor 4

As you possibly can see it’s an ordinary XHR request mixed with the FileReader JavaScript API. We use the FileReader to transform our enter to a binary knowledge, this manner our server can write it to the file system within the anticipated format. Normally individuals are utilizing a multipart-encoded type to entry recordsdata on the server, however when it’s important to work with an API it’s also possible to switch uncooked file knowledge. If you wish to study extra about XHR requests and AJAX calls, it is best to learn my earlier article.

I even have a publish about completely different file add strategies utilizing normal HTML kinds and a Vapor 4 server as a backend. I hope you’ll discover the appropriate answer that you just want to your software. 👍

Related Articles

LEAVE A REPLY

Please enter your comment!
Please enter your name here

[td_block_social_counter facebook="tagdiv" twitter="tagdivofficial" youtube="tagdiv" style="style8 td-social-boxed td-social-font-icons" tdc_css="eyJhbGwiOnsibWFyZ2luLWJvdHRvbSI6IjM4IiwiZGlzcGxheSI6IiJ9LCJwb3J0cmFpdCI6eyJtYXJnaW4tYm90dG9tIjoiMzAiLCJkaXNwbGF5IjoiIn0sInBvcnRyYWl0X21heF93aWR0aCI6MTAxOCwicG9ydHJhaXRfbWluX3dpZHRoIjo3Njh9" custom_title="Stay Connected" block_template_id="td_block_template_8" f_header_font_family="712" f_header_font_transform="uppercase" f_header_font_weight="500" f_header_font_size="17" border_color="#dd3333"]
- Advertisement -spot_img

Latest Articles