MultipartHttpServletRequest always contains a single file even when no file is selected

I have a form that is supposed to upload multiple files.

<form method="post" enctype="multipart/form-data">

    <input type="file" name="file" multiple="true">

</form>

My backend is Java/Spring. When this form is submitted, MultipartHttpServletRequest always contains a single file even when no file was selected.

My controller signature looks like

public String post(HttpServletRequest request, PageModel model, UiUtils uiUtils)

And inside the method I’m checking if the request is an instance of multipart request and cast it to MultipartHttpServletRequest.

MultipartHttpServletRequest multipartRequest = (MultipartHttpServletRequest) request;
savePatientFilesAslComplexObs(multipartRequest, encounter);;

When I used MultipartHttpServletRequest directly as a method parameter, it was always null. But casting it works.

My saveFiles method looks like

private void savePatientFilesAslComplexObs(MultipartHttpServletRequest request) throws IOException{

    List<MultipartFile> multipartFiles = request.getMultiFileMap().get("file");
    for (MultipartFile multipartFile: multipartFiles) {
        InputStream in = multipartFile.getInputStream();
        ComplexData complexData = new ComplexData(multipartFile.getOriginalFilename(), in);
        Concept concept = RegistrationUtils.getComplexConceptForPatientFiles();
        Obs obs = new Obs(encounter.getPatient(), concept, encounter.getEncounterDatetime(), encounter.getLocation());
        obs.setComplexData(complexData);
        obs.setEncounter(encounter);
        Context.getObsService().saveObs(obs, "");
    }
}

I ran my code through a debugger and placed a break point in the saveFiles method and noticed the loop is always executed even when no file is selected. So executed the following expressions to try and understand what is happening.

request.getMultiFileMap() 

That returns a LinkedHashMap with one item whose key is “file”.

request.getFileNames().hasNext()

That returns true instead of false.

request.getFileMap().size()

Returns 1.

To look at the file which included in the request, I executed

request.getMultiFileMap().get("file")

That returns an instance of CommonsMultipartFile. See image below for details of how this object looks like

enter image description here

Because of the ghost file, handler.saveObs() throws an exception when I save the form without selecting any file.

https://pastebin.com/2zmYufpC

Did you figure this out, like you usually do? :smile:

Actually I didn’t. For some reason the the request always contains a single file even when no file is selected and this caused an invalid file exception to be thrown which was propagated to the UI. So what I did is just hide the exception and proceed normally. I did log the exception though so I can remember to fix it in the future.

If you have restricted to upload only one file like multiple="false", then there will be no file object initialized for the empty selection. You can see the null during the post request if you haven’t selected any file using the form. (file == null)

But here, you are trying to get multiple files from the file selector.

<input type="file" name="file" multiple="true">

Usually, It will create an array of the file object and those files will be appended to this array. So when you check the POST request, there will be a file object initialized and all the selected files will be included into that array. (file != null but file[0] == null).

For multiple uploads (If you haven’t selected any files),

request.getMultiFileMap().size == 1 - Ther will be a null file array request.getMultiFileMap().get("file").size == 0. - Nothing inside the file array

So can you check for the array size for this file object instead of checking the FileMap size? I think FileMap().size() will check for the file object count rather than counting for the elements of that file array.

I can’t sure about this anwer :smile: .Anyway try this, It may solve your problem.