6

I want to upload multiple images in one POST request. Currently, the part of my request related to the file upload is taking one file and looks like this:

return try req.content.decode(File.self).flatMap(to: Image.self) { (file) in
    try file.data.write(to: URL(fileURLWithPath: DirectoryConfig.detect().workDir + localImageStorage + file.filename))
    return try Image(userID: user.requireID(), url: imageStorage + file.filename, filename: file.filename).save(on: req)
}

This works just fine. Now, I tried to change .decode(File.self) to .decode([File].self), and do a loop for all files.
When trying to upload images using the data[] parameter in Postman, I get the following error:

Nested form-data decoding is not supported.

How do I solve this?

LinusGeffarth
  • 27,197
  • 29
  • 120
  • 174

1 Answers1

7

Example below works well, tested multiple times already

struct MyPayload: Content {
    var somefiles: [File]
}

func myUpload(_ req: Request) -> Future<HTTPStatus> {
    let user: User = try req.requireAuthenticated()
    return try req.content.decode(MyPayload.self).flatMap { payload in
        let workDir = DirectoryConfig.detect().workDir
        return payload.somefiles.map { file in
            let url = URL(fileURLWithPath: workDir + localImageStorage + file.filename)
            try file.data.write(to: url)
            return try Image(userID: user.requireID(), url: imageStorage + file.filename, filename: file.filename).save(on: req).transform(to: ())
        }.flatten(on: req).transform(to: .ok)
    }
}

btw also you could declare your payload exactly in the function params

func myUpload(_ req: Request, _ payload: MyPayload) -> Future<HTTPStatus> {
    let user: User = try req.requireAuthenticated()
    let workDir = DirectoryConfig.detect().workDir
    return payload.somefiles.map { file in
        let url = URL(fileURLWithPath: workDir + localImageStorage + file.filename)
        try file.data.write(to: url)
        return try Image(userID: user.requireID(), url: imageStorage + file.filename, filename: file.filename).save(on: req).transform(to: ())
    }.flatten(on: req).transform(to: .ok)
}

the only difference is in declaring endpoint function on router

router.post("upload", use: myUpload)

vs

router.post(MyPayload.self, at: "upload", use: myUpload)

Then in Postman upload your files like this PostmanMultipleFiles

imike
  • 5,515
  • 2
  • 37
  • 44
  • 1
    Awesome, this works really well. Thanks so much! – LinusGeffarth Mar 12 '19 at 23:04
  • This is what I was looking for, thank you! Let me just note, that I couldn't validate the request if a file is of type `File`. Changing it to `Data` works for validations. – jstorm31 Nov 17 '19 at 14:58