Close Menu
  • Home
  • AI
  • Big Data
  • Cloud Computing
  • iOS Development
  • IoT
  • IT/ Cybersecurity
  • Tech
    • Nanotechnology
    • Green Technology
    • Apple
    • Software Development
    • Software Engineering

Subscribe to Updates

Get the latest technology news from Bigteetechhub about IT, Cybersecurity and Big Data.

    What's Hot

    Israel Hamas deal: The hostage, ceasefire, and peace agreement could have a grim lesson for future wars.

    October 14, 2025

    Astaroth: Banking Trojan Abusing GitHub for Resilience

    October 13, 2025

    ios – Differences in builds between Xcode 16.4 and Xcode 26

    October 13, 2025
    Facebook X (Twitter) Instagram
    Facebook X (Twitter) Instagram
    Big Tee Tech Hub
    • Home
    • AI
    • Big Data
    • Cloud Computing
    • iOS Development
    • IoT
    • IT/ Cybersecurity
    • Tech
      • Nanotechnology
      • Green Technology
      • Apple
      • Software Development
      • Software Engineering
    Big Tee Tech Hub
    Home»iOS Development»File upload using Vapor 4
    iOS Development

    File upload using Vapor 4

    big tee tech hubBy big tee tech hubJune 7, 2025008 Mins Read
    Share Facebook Twitter Pinterest Copy Link LinkedIn Tumblr Email Telegram WhatsApp
    Follow Us
    Google News Flipboard
    File upload using Vapor 4
    Share
    Facebook Twitter LinkedIn Pinterest Email Copy Link


    Learn how to implement a basic HTML file upload form using the Leaf template engine and Vapor, all written in Swift of course.

    Building a file upload form

    Let’s start with a basic Vapor project, we’re going to use Leaf (the Tau release) for rendering our HTML files. You should note that Tau was an experimental release, the changes were reverted from the final 4.0.0 Leaf release, but you can still use Tau if you pin the exact version in your manifest file. Tau will be published later on in a standalone repository… 🤫

    // swift-tools-version:5.3
    import PackageDescription
    
    let package = Package(
        name: "myProject",
        platforms: [
           .macOS(.v10_15)
        ],
        dependencies: [
            .package(url: " from: "4.35.0"),
            .package(url: " .exact("4.0.0-tau.1")),
            .package(url: " .exact("1.0.0-tau.1.1")),
        ],
        targets: [
            .target(
                name: "App",
                dependencies: [
                    .product(name: "Leaf", package: "leaf"),
                    .product(name: "LeafKit", package: "leaf-kit"),
                    .product(name: "Vapor", package: "vapor"),
                ],
                swiftSettings: [
                    .unsafeFlags(["-cross-module-optimization"], .when(configuration: .release))
                ]
            ),
            .target(name: "Run", dependencies: [.target(name: "App")]),
            .testTarget(name: "AppTests", dependencies: [
                .target(name: "App"),
                .product(name: "XCTVapor", package: "vapor"),
            ])
        ]
    )
    

    Now if you open the project with Xcode, don’t forget to setup a custom working directory first, because we’re going to create templates and Leaf will look for those view files under the current working directory by default. We are going to build a very simple index.leaf file, you can place it into the Resources/Views directory.

    
    
      
        
        
        File upload example
      
      
        
    
        
      
    
    

    As you can see, it’s a standard file upload form, when you want to upload files using the browser you always have to use the multipart/form-data encryption type. The browser will pack every field in the form (including the file data with the original file name and some meta info) using a special format and the server application can parse the contents of this. Fortunately Vapor has built-in support for easy decoding multipart form data values. We are going to use the POST /upload route to save the file, let’s setup the router first so we can render our main page and we are going to prepare our upload path as well, but we will respond with a dummy message for now.

    import Vapor
    import Leaf
    
    public func configure(_ app: Application) throws {
    
        /// config max upload file size
        app.routes.defaultMaxBodySize = "10mb"
        
        /// setup public file middleware (for hosting our uploaded files)
        app.middleware.use(FileMiddleware(publicDirectory: app.directory.publicDirectory))
        
        /// setup Leaf template engine
        LeafRenderer.Option.caching = .bypass
        app.views.use(.leaf)
    
        /// index route
        app.get { req in
            req.leaf.render(template: "index")
        }
        
        /// upload handler
        app.post("upload") { req in
            "Upload file..."
        }
    }
    

    You can put the snippet above into your configure.swift file then you can try to build and run your server and visit http://localhost:8080, then try to upload any file. It won’t actually upload the file, but at least we are prepared to write our server side Swift code to process the incoming form data. ⬆️

    File upload handler in Vapor

    Now that we have a working uploader form we should parse the incoming data, get the contents of the file and place it under our Public directory. You can actually move the file anywhere on your server, but for this example we are going to use the Public directory so we can simply test if everthing works by using the FileMiddleware. If you don’t know, the file middleware serves everything (publicly available) that is located inside your Public folder. Let’s code.

    app.post("upload") { req -> EventLoopFuture in
        struct Input: Content {
            var file: File
        }
        let input = try req.content.decode(Input.self)
        
        let path = app.directory.publicDirectory + input.file.filename
        
        return req.application.fileio.openFile(path: path,
                                               mode: .write,
                                               flags: .allowFileCreation(posixMode: 0x744),
                                               eventLoop: req.eventLoop)
            .flatMap { handle in
                req.application.fileio.write(fileHandle: handle,
                                             buffer: input.file.data,
                                             eventLoop: req.eventLoop)
                    .flatMapThrowing { _ in
                        try handle.close()
                        return input.file.filename
                    }
            }
    }
    

    So, let me explain what just happened here. First we define a new Input type that will contain our file data. There is a File type in Vapor that helps us decoding multipart file upload forms. We can use the content of the request and decode this type. We gave the file name to the file input form previously in our leaf template, but of course you can change it, but if you do so you also have to align the property name inside the Input struct.

    After we have an input (please note that we don’t validate the submitted request yet) we can start uploading our file. We ask for the location of the public directory, we append the incoming file name (to keep the original name, but you can generate a new name for the uploaded file as well) and we use the non-blocking file I/O API to create a file handler and write the contents of the file into the disk. The fileio API is part of SwiftNIO, which is great because it’s a non-blocking API, so our server will be more performant if we use this instead of the regular FileManager from the Foundation framework. After we opened the file, we write the file data (which is a ByteBuffer object, bad naming…) and finally we close the opened file handler and return the uploaded file name as a future string. If you haven’t heard about futures and promises you should read about them, because they are everywhere on the server side Swift world. Can’t wait for async / awake support, right? 😅

    We will enhance the upload result page just a little bit. Create a new result.leaf file inside the views directory.

    
    
      
        
        
        File uploaded
      
      
        
    
        #if(isImage):
            File upload using Vapor 4
    #else: Show me!
    #endif Upload new one

    So we’re going to check if the uploaded file has an image extension and pass an isImage parameter to the template engine, so we can display it if we can assume that the file is an image, otherwise we’re going to render a simple link to view the file. Inside the post upload handler method we are going to add a date prefix to the uploaded file so we will be able to upload multiple files even with the same name.

    app.post("upload") { req -> EventLoopFuture in
        struct Input: Content {
            var file: File
        }
        let input = try req.content.decode(Input.self)
    
        guard input.file.data.readableBytes > 0 else {
            throw Abort(.badRequest)
        }
    
        let formatter = DateFormatter()
        formatter.dateFormat = "y-m-d-HH-MM-SS-"
        let prefix = formatter.string(from: .init())
        let fileName = prefix + input.file.filename
        let path = app.directory.publicDirectory + fileName
        let isImage = ["png", "jpeg", "jpg", "gif"].contains(input.file.extension?.lowercased())
    
        return req.application.fileio.openFile(path: path,
                                               mode: .write,
                                               flags: .allowFileCreation(posixMode: 0x744),
                                               eventLoop: req.eventLoop)
            .flatMap { handle in
                req.application.fileio.write(fileHandle: handle,
                                             buffer: input.file.data,
                                             eventLoop: req.eventLoop)
                    .flatMapThrowing { _ in
                        try handle.close()
                    }
                    .flatMap {
                        req.leaf.render(template: "result", context: [
                            "fileUrl": .string(fileName),
                            "isImage": .bool(isImage),
                        ])
                    }
            }
    }
    

    If you run this example you should be able to view the image or the file straight from the result page.

    Multiple file upload using Vapor

    By the way, you can also upload multiple files at once if you add the multiple attribute to the HTML file input field and use the files[] value as name.


    To support this we have to alter our upload method, don’t worry it’s not that complicated as it looks at first sight. 😜

    app.post("upload") { req -> EventLoopFuture in
        struct Input: Content {
            var files: [File]
        }
        let input = try req.content.decode(Input.self)
    
        let formatter = DateFormatter()
        formatter.dateFormat = "y-m-d-HH-MM-SS-"
        let prefix = formatter.string(from: .init())
        
        struct UploadedFile: LeafDataRepresentable {
            let url: String
            let isImage: Bool
            
            var leafData: LeafData {
                .dictionary([
                    "url": url,
                    "isImage": isImage,
                ])
            }
        }
        
        let uploadFutures = input.files
            .filter { $0.data.readableBytes > 0 }
            .map { file -> EventLoopFuture in
                let fileName = prefix + file.filename
                let path = app.directory.publicDirectory + fileName
                let isImage = ["png", "jpeg", "jpg", "gif"].contains(file.extension?.lowercased())
                
                return req.application.fileio.openFile(path: path,
                                                       mode: .write,
                                                       flags: .allowFileCreation(posixMode: 0x744),
                                                       eventLoop: req.eventLoop)
                    .flatMap { handle in
                        req.application.fileio.write(fileHandle: handle,
                                                     buffer: file.data,
                                                     eventLoop: req.eventLoop)
                            .flatMapThrowing { _ in
                                try handle.close()
                                return UploadedFile(url: fileName, isImage: isImage)
                            }
                        
                    }
            }
    
        return req.eventLoop.flatten(uploadFutures).flatMap { files in
            req.leaf.render(template: "result", context: [
                "files": .array(files.map(\.leafData))
            ])
        }
    }
    

    The trick is that we have to parse the input as an array of files and turn every possible upload into a future upload operation. We can filter the upload candidates by readable byte size, then we map the files into futures and return an UploadedFile result with the proper file URL and is image flag. This structure is a LeafDataRepresentable object, because we want to pass it as a context variable to our result template. We also have to change that view once again.

    
    
      
        
        
        Files uploaded
      
      
        
    
        #for(file in files):
            #if(file.isImage):
            #(file
    #else: #(file.url)
    #endif #endfor Upload new files

    Well, I know this is a dead simple implementation, but it’s great if you want to practice or learn how to implement file uploads using server side Swift and the Vapor framework. You can also upload files directly to a cloud service using this technique, there is a library called Liquid, which is similar to Fluent, but for file storages. Currently you can use Liquid to upload files to the local storage or you can use an AWS S3 bucket or you can write your own driver using LiquidKit. The API is pretty simple to use, after you configure the driver you can upload files with just a few lines of code.



    Source link

    File upload Vapor
    Follow on Google News Follow on Flipboard
    Share. Facebook Twitter Pinterest LinkedIn Tumblr Email Copy Link
    tonirufai
    big tee tech hub
    • Website

    Related Posts

    ios – Differences in builds between Xcode 16.4 and Xcode 26

    October 13, 2025

    ios – Apple mapkit route function dose not works in China

    October 12, 2025

    swift – Does UIDevice.current.identifierForVendor change after iCloud backup and restore on another iOS device?

    October 11, 2025
    Add A Comment
    Leave A Reply Cancel Reply

    Editors Picks

    Israel Hamas deal: The hostage, ceasefire, and peace agreement could have a grim lesson for future wars.

    October 14, 2025

    Astaroth: Banking Trojan Abusing GitHub for Resilience

    October 13, 2025

    ios – Differences in builds between Xcode 16.4 and Xcode 26

    October 13, 2025

    How to run RAG projects for better data analytics results

    October 13, 2025
    Advertisement
    About Us
    About Us

    Welcome To big tee tech hub. Big tee tech hub is a Professional seo tools Platform. Here we will provide you only interesting content, which you will like very much. We’re dedicated to providing you the best of seo tools, with a focus on dependability and tools. We’re working to turn our passion for seo tools into a booming online website. We hope you enjoy our seo tools as much as we enjoy offering them to you.

    Don't Miss!

    Israel Hamas deal: The hostage, ceasefire, and peace agreement could have a grim lesson for future wars.

    October 14, 2025

    Astaroth: Banking Trojan Abusing GitHub for Resilience

    October 13, 2025

    Subscribe to Updates

    Get the latest technology news from Bigteetechhub about IT, Cybersecurity and Big Data.

      • About Us
      • Contact Us
      • Disclaimer
      • Privacy Policy
      • Terms and Conditions
      © 2025 bigteetechhub.All Right Reserved

      Type above and press Enter to search. Press Esc to cancel.