Search code examples
javaspring-mvcfile-uploadapache-commons-fileupload

Persist CommonsMultipartFile to database


I am trying to persist a uploaded file to database using JPA. The most "natural" way (to me) is to define domain object as:

@Entity
class UploadFile {
  ...
  public CommonsMultipartFile getFileData()
  {
    return fileData;
  }
}

But this won't work since there is not such a database mapping type. Searching online, I found that people seems to adapt one of the two methods:

  • Define the field as java.sql.blob;
  • Define the field as byte[]

In the @Controller class, the incoming HttpServletRequest gets cast to MultipartHttpServletRequest in order to access the MultipartFile and convert it back to byte[] stream.

However, with this scheme, I have "random" results which puzzles me: from time to time, I ran into "bean property not readable" error, possible mismatch of return type of getter method on the byte[] field. I double and triple checked my Bean definition, and couldn't find anything wrong.

I guess my questions are two folds: (1) Any idea why this error can happen in this context? (2) more importantly, what are the "recommended" way of handling uploaded file like this?

thanks

Oliver


Solution

  • You are correct that either a java.sql.blob or a byte[] would be the most appropriate way to store an uploaded file in a database.

    You should not store a MultipartFile or a CommonsMultipartFile in a database because these are intended to be temporary objects. Note this from the MultipartFile javadoc:

    The file contents are either stored in memory or temporarily on disk. In either case, the user is responsible for copying file contents to a session-level or persistent store as and if desired. The temporary storages will be cleared at the end of request processing.

    I don't quite follow what you're doing with your HttpServletRequest but it doesn't sound like the easiest way to handle the upload. The easiest way to handle an upload is to make the MultipartFile a parameter on your controller method (if you're using Spring 3.0 or later, I think):

    @RequestMapping(value = "/upload", method = RequestMethod.POST)
    public void exampleFileUpload(@RequestParam("file") MultipartFile file) {
        if (!file.isEmpty()) {
            try {
                byte[] fileBytes = file.getBytes();
    
                // Persist those bytes using JPA here
    
            } catch (IOException e) {
                // Exception handling here
            }
        }
    }
    

    This should work reliably because you've extracted the bytes into your byte[] array before the request has finished — and thus before the any temporary files used to handle the upload have been deleted.