For 99.9% of use cases, Paulo Lopes' answer is the absolutely correct one.
The main gotcha for it, is that the uploaded file is stored in temporary space on the file system - you have to make sure there's enough space available for all the files users will upload; you have to make sure you put a proper limit on the max upload size so attackers can't DoS you; you have to handle the file and delete it when its done.
If all of this worries you and you are willing to put in the extra work, here's how to get a Vert.x ReadStream
of an upload - which you can then read from and push the data somewhere that makes sense, without incurring the storage and IO associated with using RoutingContext.fileUploads()
:
Important Note First
If you don't want BodyHandler
to consume the uploads and save them to local files - do not let it handle the uploads. There are three main ways to do it:
- Don't set a body handler on the routes you want to handle uploads on (or any parent route).
- Create your body handler with no file upload support, using
BodyHandler.create(false)
.
- Set up the body handler in a route ordered after the route where you are going to handle the uploads. This is the most problematic as the body handler will break the request processing if it is called after the request has already ended streaming, and there isn't even an error (as of Vert.x 4.4.4 - the next version has this fixed, and will issue an error). To use this method you have to wrangle the request by pausing the request reading, and then either you handle the upload and resume request reading yourself - or you need to let the body handler resume the request automatically as needed.
Here I will demonstrate option 3.
Router router = Router.router(vertx);
// Pause the request reading so we can register for handling the body later
router.route("/*").handler(ctx -> { ctx.request().pause(); ctx.next(); });
// handle the upload
router.post("/upload").handler(ctx -> {
ctx.request().setExpectMultipart(true) // this sets up the upload handlers
.uploadHandler(upload -> { // register our upload handler
// this handler will be called once for each file found in the request
var filename = upload.filename();
var contentType = upload.contentType();
consumeUpload(upload.pipe()); // upload is a ReadStream, so read it
})
.endHandler(__ -> {
// somehow verify that all uploads were consumed successfully
// (or figure out the correct error response)
ctx.response().end(); // do something more meaningful here
})
.resume(); // after we have setup our handlers, resume the request
});
// have a body handler to handle all the other routes
// it will resume the request automatically so the body will be available
// for outher routes
router.route("/*").handler(BodyHandler.create());
// ... what ever else routes you have