1

I am writing the code for multipart file upload using the vertx.io .

In Spring boot my code is as follows. I want to write similar in vertex.io

@RequestMapping(value = "/upload", headers=("content-type=multipart/*") ,method = RequestMethod.POST)
public ImportExportResponse upload(@RequestParam("file") MultipartFile inputFile){
    log.info(" upload service method starts  ");
    ImportExportResponse response = new ImportExportResponse();
    FileOutputStream fileOutputStream=null;
    try{
        File outputFile = new File(FILE_LOCATION+inputFile.getOriginalFilename());
        fileOutputStream = new FileOutputStream(outputFile);
        fileOutputStream.write(inputFile.getBytes());   
        fileOutputStream.close();

        response.setStatus(ImportExportConstants.ResponseStatus.SUCCESS.name());
        response.setErrorMessage(EMPTY);

    }catch (Exception e) {
        log.error("Exception while upload the file . "+e.getMessage());
        response.setStatus(ImportExportConstants.ResponseStatus.ERROR.name());
        response.setErrorMessage(errorMap.get(SYSTEM_ERROR_CODE));          
    }
    log.info(" upload service method ends. file is copied to a temp folder ");
    return response;
}
Paulo Lopes
  • 5,845
  • 22
  • 31
user3352615
  • 119
  • 2
  • 5
  • 13

2 Answers2

13

Here is the same but in vert.x:

Router router = Router.router(vertx);

// Enable multipart form data parsing
router.post("/upload").handler(BodyHandler.create()
  .setUploadsDirectory(FILE_LOCATION));

// handle the form
router.post("/upload").handler(ctx -> {
  // in your example you only handle 1 file upload, here you can handle
  // any number of uploads
  for (FileUpload f : ctx.fileUploads()) {
    // do whatever you need to do with the file (it is already saved
    // on the directory you wanted...
    System.out.println("Filename: " + f.fileName());
    System.out.println("Size: " + f.size());
  }

  ctx.response().end();
});

For more examples you can always see the vertx-examples repo.

Paulo Lopes
  • 5,845
  • 22
  • 31
  • Thanks for the help . I am able to upload the file and change the directory but now how can i add the constraint that my file size should not be more than 5MB. If it is more than 5 MB it should not store the file in directory. – user3352615 Aug 30 '16 at 17:49
  • 1
    Use .setBodyLimit() for that – Alexey Soshin Aug 30 '16 at 19:05
  • When I am uploading the file .This method fileUpload.uploadedFileName(); is giving me the full path with complete location For Example:C:\Users\\fileToBeStored\baf1005d-d000-4e8f-b590-f97805b3969c . I want instead of full directory path it should return me baf1005d-d000-4e8f-b590-f97805b3969c . How can i do that ? – user3352615 Sep 02 '16 at 18:25
  • You can always create a `java.io.File` object from the string and extract what you need. – Paulo Lopes Sep 05 '16 at 10:19
  • Now lets say if i am doing multiple fie upload it should skip the file to store in physical loaction if size of file is greater than 5 MB . if (fileUpload.size()>5000000){ errorMsg="File is greater than 5MB ."+size; deleteFileUploads(); or skip that particular file } – user3352615 Sep 08 '16 at 19:28
  • If you want to skip storing you need to define the max allowed input size for the whole request as @AlexeySoshin said. When you're iterating the fileUploads list the files have already been stored in the tmp folder so if they are greater than you would like you need to just delete them. – Paulo Lopes Sep 09 '16 at 13:07
  • On the scenario of multiple files uploading, how can we detect which file is of which key? We need to know the exact uploaded file path with the help of its key and should be stored on the DB. – RaiBnod Sep 06 '18 at 18:10
  • I am trying to OCTET_STREAM with vertx pipeTo method https://stackoverflow.com/questions/73812830/incorrect-zip-file-being-created-in-vertx-3-x-from-a-http-request. How can we use the above example for non multipart request data ? – Anshul Sep 22 '22 at 18:30
0

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:

  1. Don't set a body handler on the routes you want to handle uploads on (or any parent route).
  2. Create your body handler with no file upload support, using BodyHandler.create(false).
  3. 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
Guss
  • 30,470
  • 17
  • 104
  • 128