简体   繁体   中英

Uploading a file inside a Spring Web Flow - error handling?

I'm using Spring Webflow and org.springframework.web.multipart.commons.CommonsMultipartFile to upload a file from a web page. The upload works fine when the input form passes validation. The problem comes when the form fails validation and the Web Flow tries to redisplay the page. It appears that it tries reload the file upload control it looks for the temporary file the upload used, and it's not there. Looking at the actual MultipartFile.java source I see this comment:

The temporary storages will be cleared at the end of request processing.

So instead of redisplaying the web form the app throws this error below. What's the best way to handle this inside of Web Flow (remembering that Web Flow doesn't give you the same level of control as "regular" MVC). I'm fine with throwing away whatever's in the MultipartFile object but I can't figure out what to initialize it to.

Here's what I'm getting right now:

org.springframework.webflow.execution.repository.FlowExecutionRestorationFailureException: A problem occurred restoring the flow execution with key 'e1s5'
    at org.springframework.webflow.execution.repository.snapshot.SerializedFlowExecutionSnapshotFactory.restoreExecution(SerializedFlowExecutionSnapshotFactory.java:82)
    at org.springframework.webflow.execution.repository.snapshot.AbstractSnapshottingFlowExecutionRepository.restoreFlowExecution(AbstractSnapshottingFlowExecutionRepository.java:89)
    at org.springframework.webflow.execution.repository.impl.DefaultFlowExecutionRepository.getFlowExecution(DefaultFlowExecutionRepository.java:115)
    at org.springframework.webflow.executor.FlowExecutorImpl.resumeExecution(FlowExecutorImpl.java:168)
    at org.springframework.webflow.mvc.servlet.FlowHandlerAdapter.handle(FlowHandlerAdapter.java:183)
    at org.springframework.web.servlet.DispatcherServlet.doDispatch(DispatcherServlet.java:925)
    at org.springframework.web.servlet.DispatcherServlet.doService(DispatcherServlet.java:856)
    at org.springframework.web.servlet.FrameworkServlet.processRequest(FrameworkServlet.java:920)
    at org.springframework.web.servlet.FrameworkServlet.doGet(FrameworkServlet.java:816)
    at javax.servlet.http.HttpServlet.service(HttpServlet.java:621)
    at org.springframework.web.servlet.FrameworkServlet.service(FrameworkServlet.java:801)
    at javax.servlet.http.HttpServlet.service(HttpServlet.java:728)
    at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:305)
    at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:210)
    at net.bull.javamelody.MonitoringFilter.doFilter(MonitoringFilter.java:165)
    at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:243)
    at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:210)
    at net.bull.javamelody.MonitoringFilter.doFilter(MonitoringFilter.java:203)
    at net.bull.javamelody.MonitoringFilter.doFilter(MonitoringFilter.java:181)
    at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:243)
    at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:210)
    at org.apache.catalina.core.StandardWrapperValve.invoke(StandardWrapperValve.java:222)
    at org.apache.catalina.core.StandardContextValve.invoke(StandardContextValve.java:123)
    at org.apache.catalina.authenticator.AuthenticatorBase.invoke(AuthenticatorBase.java:502)
    at org.apache.catalina.core.StandardHostValve.invoke(StandardHostValve.java:171)
    at org.apache.catalina.valves.ErrorReportValve.invoke(ErrorReportValve.java:99)
    at org.apache.catalina.valves.AccessLogValve.invoke(AccessLogValve.java:953)
    at org.apache.catalina.core.StandardEngineValve.invoke(StandardEngineValve.java:118)
    at org.apache.catalina.connector.CoyoteAdapter.service(CoyoteAdapter.java:408)
    at org.apache.coyote.http11.AbstractHttp11Processor.process(AbstractHttp11Processor.java:1023)
    at org.apache.coyote.AbstractProtocol$AbstractConnectionHandler.process(AbstractProtocol.java:589)
    at org.apache.tomcat.util.net.AprEndpoint$SocketProcessor.run(AprEndpoint.java:1852)
    at java.util.concurrent.ThreadPoolExecutor$Worker.runTask(ThreadPoolExecutor.java:886)
    at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:908)
    at java.lang.Thread.run(Thread.java:662)
Caused by: org.springframework.webflow.execution.repository.snapshot.SnapshotUnmarshalException: IOException thrown deserializing the flow execution stored in this snapshot -- this should not happen!
    at org.springframework.webflow.execution.repository.snapshot.SerializedFlowExecutionSnapshot.unmarshal(SerializedFlowExecutionSnapshot.java:101)
    at org.springframework.webflow.execution.repository.snapshot.SerializedFlowExecutionSnapshotFactory.restoreExecution(SerializedFlowExecutionSnapshotFactory.java:80)
    ... 34 more
Caused by: java.io.FileNotFoundException: C:\Users\wombat\.IntelliJIdea12\system\tomcat\Unnamed_workspace_credapp_3\work\Catalina\localhost\Application\upload_57256533_a72f_42e6_97a6_7722c4872b68_00000044.tmp (The system cannot find the file specified)
    at java.io.FileInputStream.open(Native Method)
    at java.io.FileInputStream.<init>(FileInputStream.java:120)
    at org.apache.commons.fileupload.disk.DiskFileItem.readObject(DiskFileItem.java:663)
    at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
    at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:39)
    at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:25)
    at java.lang.reflect.Method.invoke(Method.java:597)
    at java.io.ObjectStreamClass.invokeReadObject(ObjectStreamClass.java:969)
    at java.io.ObjectInputStream.readSerialData(ObjectInputStream.java:1848)
    at java.io.ObjectInputStream.readOrdinaryObject(ObjectInputStream.java:1752)
    at java.io.ObjectInputStream.readObject0(ObjectInputStream.java:1328)
    at java.io.ObjectInputStream.defaultReadFields(ObjectInputStream.java:1946)
    at java.io.ObjectInputStream.readSerialData(ObjectInputStream.java:1870)
    at java.io.ObjectInputStream.readOrdinaryObject(ObjectInputStream.java:1752)
    at java.io.ObjectInputStream.readObject0(ObjectInputStream.java:1328)
    at java.io.ObjectInputStream.defaultReadFields(ObjectInputStream.java:1946)
    at java.io.ObjectInputStream.readSerialData(ObjectInputStream.java:1870)
    at java.io.ObjectInputStream.readOrdinaryObject(ObjectInputStream.java:1752)
    at java.io.ObjectInputStream.readObject0(ObjectInputStream.java:1328)
    at java.io.ObjectInputStream.readObject(ObjectInputStream.java:350)
    at java.util.HashMap.readObject(HashMap.java:1030)
    at sun.reflect.GeneratedMethodAccessor44.invoke(Unknown Source)
    at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:25)
    at java.lang.reflect.Method.invoke(Method.java:597)
    at java.io.ObjectStreamClass.invokeReadObject(ObjectStreamClass.java:969)
    at java.io.ObjectInputStream.readSerialData(ObjectInputStream.java:1848)
    at java.io.ObjectInputStream.readOrdinaryObject(ObjectInputStream.java:1752)
    at java.io.ObjectInputStream.readObject0(ObjectInputStream.java:1328)
    at java.io.ObjectInputStream.defaultReadFields(ObjectInputStream.java:1946)
    at java.io.ObjectInputStream.defaultReadObject(ObjectInputStream.java:479)
    at org.springframework.webflow.core.collection.LocalAttributeMap.readObject(LocalAttributeMap.java:331)
    at sun.reflect.GeneratedMethodAccessor97.invoke(Unknown Source)
    at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:25)
    at java.lang.reflect.Method.invoke(Method.java:597)
    at java.io.ObjectStreamClass.invokeReadObject(ObjectStreamClass.java:969)
    at java.io.ObjectInputStream.readSerialData(ObjectInputStream.java:1848)
    at java.io.ObjectInputStream.readOrdinaryObject(ObjectInputStream.java:1752)
    at java.io.ObjectInputStream.readObject0(ObjectInputStream.java:1328)
    at java.io.ObjectInputStream.readObject(ObjectInputStream.java:350)
    at org.springframework.webflow.engine.impl.FlowSessionImpl.readExternal(FlowSessionImpl.java:153)
    at java.io.ObjectInputStream.readExternalData(ObjectInputStream.java:1791)
    at java.io.ObjectInputStream.readOrdinaryObject(ObjectInputStream.java:1750)
    at java.io.ObjectInputStream.readObject0(ObjectInputStream.java:1328)
    at java.io.ObjectInputStream.readObject(ObjectInputStream.java:350)
    at java.util.LinkedList.readObject(LinkedList.java:964)
    at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
    at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:39)
    at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:25)
    at java.lang.reflect.Method.invoke(Method.java:597)
    at java.io.ObjectStreamClass.invokeReadObject(ObjectStreamClass.java:969)
    at java.io.ObjectInputStream.readSerialData(ObjectInputStream.java:1848)
    at java.io.ObjectInputStream.readOrdinaryObject(ObjectInputStream.java:1752)
    at java.io.ObjectInputStream.readObject0(ObjectInputStream.java:1328)
    at java.io.ObjectInputStream.readObject(ObjectInputStream.java:350)
    at org.springframework.webflow.engine.impl.FlowExecutionImpl.readExternal(FlowExecutionImpl.java:304)
    at java.io.ObjectInputStream.readExternalData(ObjectInputStream.java:1791)
    at java.io.ObjectInputStream.readOrdinaryObject(ObjectInputStream.java:1750)
    at java.io.ObjectInputStream.readObject0(ObjectInputStream.java:1328)
    at java.io.ObjectInputStream.readObject(ObjectInputStream.java:350)
    at org.springframework.webflow.execution.repository.snapshot.SerializedFlowExecutionSnapshot.deserialize(SerializedFlowExecutionSnapshot.java:194)
    at org.springframework.webflow.execution.repository.snapshot.SerializedFlowExecutionSnapshot.unmarshal(SerializedFlowExecutionSnapshot.java:99)
    ... 35 more

The answer to this turned out to be embarrassingly, even annoyingly, simple. If validation has failed, set the MultipartFile member of your form object to null before returning from the validation.

myObject.setFileUpload(null);

When the response is sent back to the client, the form upload control will be re-created by Spring.

Instead of set MultipartFile at null:

myObject.setFileUpload(null); ( https://stackoverflow.com/a/22874667/7678594 )

you can declare the attribute as "transient" in your class:

private transient CommonsMultipartFile fileUpload;

These exceptions are thrown at data binding and before the validator is called. This can occur 1) When file uploaded by you has exceeded the maxUploadSize set in multipartresolver bean definition in the configuration and returned to validator and accessed the multipartfile. 2) Request(uploading over network) gets timedout due to network latency.

Extend CommonsMultipartResolver and overwrite the resolveMultipart method in it.

    public MultipartHttpServletRequest resolveMultipart(HttpServletRequest request) throws MultipartException {
        String encoding = determineEncoding(request);
        FileUpload fileUpload = prepareFileUpload(encoding);
        try {
        List fileItems = ((ServletFileUpload) fileUpload).parseRequest(request);
        MultipartParsingResult parsingResult = parseFileItems(fileItems, encoding);
        return new DefaultMultipartHttpServletRequest(
                request, parsingResult.getMultipartFiles(), parsingResult.getMultipartParameters(), parsingResult.getMultipartParameterContentTypes());
        } catch (FileUploadBase.SizeLimitExceededException ex) {
            throw new MaxUploadSizeExceededException(fileUpload.getSizeMax(), ex);
        }
        catch (FileUploadException ex) {
            throw new MultipartException("Could not parse multipart servlet request", ex);
        }
    }

If the upload is at only one view or you are good to direct to erro page then use the exception resolver as: public class YourExceptionResolver extends SimpleMappingExceptionResolver { @Override public ModelAndView resolveException(HttpServletRequest request, HttpServletResponse response, Object handler, Exception exception) {

        if(exception instanceof MaxUploadSizeExceededException) {
        //If (exception instanceof FileUploadBase.SizeLimitExceededException)
            String errorMessage = "ERROR: Filesize exceeded.");
            HashMap<String, Object> model = new HashMap<String, Object>();
            model.put("errorMessage", errorMessage);
            return new ModelAndView("jsp/uploadView", model);
        } else {
            return super.resolveException(request, response, handler, exception); 
        }
    }

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM