Problem with Spring FileUpload

2020-02-08 09:30发布

问题:

I have the following block of code which is handling my file upload of a photo that I am using in my Spring MVC web application. I am using Spring MVC CommonsMultipartFileResolver to handle file uploads.

if(model.getPhoto() != null){
    if(!model.getPhoto().isEmpty()){
        MultipartFile file = model.getPhoto();
        String fileName = file.getOriginalFilename();
        String filePath = baseDirectory + fileName;
        FileOutputStream fos = new FileOutputStream(filePath);
         try 
         {
            fos.write(file.getBytes());
            agentProfile.setPhotoUri(fileName);
         } 
         catch (IllegalStateException e) 
         {
            System.out.println(e);

         }
         finally   
         {
             fos.close();
         }
    }
}

In my app-servlet.xml file I have the following code to configure the MultipartFile resolver bean.

 <bean id="multipartResolver"   class="org.springframework.web.multipart.commons.CommonsMultipartResolver">
 </bean>

I am experiencing some random problems when I am uploading photos.

1)If I go to upload a smaller photo, around 3 kb or so, It will upload successfully.

2)If I go to upload a little larger photo, it will create the file in the directory, but with a size of 0 bytes and will give the following error message.

 java.lang.IllegalStateException: File has been moved - cannot be read again
org.springframework.web.multipart.commons.CommonsMultipartFile.getBytes(CommonsMultipartFile.java:112)
com.mmz.admin.mvc.controller.AddAgentController.processFinish(AddAgentController.java:145)
org.springframework.web.servlet.mvc.AbstractWizardFormController.validatePagesAndFinish(AbstractWizardFormController.java:642)
org.springframework.web.servlet.mvc.AbstractWizardFormController.processFormSubmission(AbstractWizardFormController.java:492)
org.springframework.web.servlet.mvc.AbstractFormController.handleRequestInternal(AbstractFormController.java:265)
org.springframework.web.servlet.mvc.AbstractController.handleRequest(AbstractController.java:153)
org.springframework.web.servlet.mvc.SimpleControllerHandlerAdapter.handle(SimpleControllerHandlerAdapter.java:48)
org.springframework.web.servlet.DispatcherServlet.doDispatch(DispatcherServlet.java:874)
org.springframework.web.servlet.DispatcherServlet.doService(DispatcherServlet.java:808)
org.springframework.web.servlet.FrameworkServlet.processRequest(FrameworkServlet.java:476)
org.springframework.web.servlet.FrameworkServlet.doPost(FrameworkServlet.java:441)
javax.servlet.http.HttpServlet.service(HttpServlet.java:637)
javax.servlet.http.HttpServlet.service(HttpServlet.java:717)

I have tried a couple different options configuring the Multipart resolver such as switching it to handle a CommonsMultipartFile object as oppose to a plain MultipartFile object, but nothing changed.

I have also tried to manuall configure the maximum upload size in the CommonsMultipartFileResolver bean with the following property.

 <property name="maxUploadSize" value="1024000000"/>  

nothing changed as well. I am not sure what the CommonsMultipartResolver defaults to as far as size of the file that can be uploaded, but that is not my question.

I have been told that the problem I am experiencing is due to a problem in the Multipart parser/handler that spring is using. I had a recent post about this same problem, and because new information was found, wanted to repost with the new information. The old post can be found at CommonsMultipartFileResolver Problem

I feel that I have checked nearly every resource on the internet to find additional documentation, but am unable to figure out the problem.

Please help me figure out what is going on with this, and if there is a better, simpler solution to maybe explorer those options, but I would prefer to stay with my current method if I can find a solution.

EDIT Note- I have been experimenting with different size photos to upload, and I believe that the limit that it is allowing me to upload is around 10Kb. Anything larger then 10Kb is causing it to break and give me the error above.

回答1:

After doing a lot of research, I solved my problem. It turns out that there is not a default limit set for the maximum amount of bytes that you can upload using CommonsMultipartFileResolver Of course you can specify in your bean whatever you want for this amount by setting the following property.

<property name="maxUploadSize" value="99999999999"/>

There is also a property maxInMemorySize that allows you to specify the maximum size allowed before files are written to disk. Although this works the same way as the max upload size, if you do not specify an amount, it defaults to 1024 bytes. This would explain it breaking if I attempted to upload a large file.

In order to allow files above 1024 bytes to be uploaded, you need to increase the maxInMemorySize value to whatever you need like the following...

This is what took care of my problem. I learned that this property defaults to 1024 when I was reviewing documentation for CommonsFileUpload Documentation.

You can view this documentation at CommonsFileUpload Documentation

I hope this helps anybody, as there is not very good documentation on using CommonsMultipartFile.



回答2:

I notice this error only occurs when the file is over 1024 bytes AND you try to read the file twice. As CitadelCSAlum mentions, setting maxInMemorySize = maxUploadSize will fix this issue, but one should keep in mind memory usage. If memory is a concern, another option is to write the multipart file data to a temp file on the first read and use that file for subsequent reads. If you don't read the twice you should not need to increase maxInMemorySize.



回答3:

The exception you reference in your question states: "File has been moved - cannot be read again". This is because we are trying to read inputstream more than one time from multipart file.

I also faced this issue at one time, and in my case, first I validated the content of the file, and after that I tried to save it using "transferTo" method in Spring MultiPart. This exception comes when I try to use "transferTo" method. Here I am calling for inputstream twice.

I do not face this issue when file size is too small. In "transferTo" method there is an internal call for "isAvailable" method. Please follow the code segment below:

    protected boolean More ...isAvailable() {
            // If in memory, it's available.
        if (this.fileItem.isInMemory()) {
            return true;
        }
        // Check actual existence of temporary file.
        if (this.fileItem instanceof DiskFileItem) {
            return ((DiskFileItem) this.fileItem).getStoreLocation().exists();
        }
        // Check whether current file size is different than original one.
        return (this.fileItem.getSize() == this.size);
    }

link: http://grepcode.com/file/repo1.maven.org/maven2/org.springframework/spring-web/3.2.1.RELEASE/org/springframework/web/multipart/commons/CommonsMultipartFile.java#CommonsMultipartFile.isAvailable%28%29

Observations:

  1. If it is too small, spring save it in memory and when we ask for the file it retrive from memory. We can ask for it multiple times because file is in memory.

  2. If it is large enough, Spring will save it as a temporary file which we do not know the location, but after we read inputstream once that file may be deleted internally by Spring. Then when we ask for the second time that error says "cannot be read again".

So my solution is first I have to save it in server loaction using "transferTo" method and retrive that local file to validate or any other second time need.

I think it is not good to increase "maxUploadSize" in "multipartResolver" bean as it consumes more memory if the file is too large.