Handling MaxUploadSizeExceededException can not stop uploading file
Asked Answered
M

4

13

I want to check size of uploading files and prevent files loaded in memory entirely. I'm using CommonsMultipartFile. The uploaded file will be processed and persisted in DB. AbstractCoupleUploadController class handles incoming request that containing files:

public abstract class AbstractCoupleUploadController<T extends Serializable> extends RemoteServiceServlet implements ServletContextAware,
        UploadServlet<WorkshopHistoryModel>
{
    ...

    @RequestMapping(method={RequestMethod.GET,RequestMethod.POST})
    public ModelAndView handleRequest(@RequestParam("firstFile") CommonsMultipartFile firstFile,
            @RequestParam("secondFile") CommonsMultipartFile secondFile, HttpServletRequest request, HttpServletResponse response)
    {
        synchronized(this)
        {
            initThreads();
            perThreadRequest.set(request);
            perThreadResponse.set(response);
        }

        handleUpload(firstFile,secondFile,request,response);
        response.getWriter().flush();
        response.flushBuffer();
        return null;
    }

    private void handleUpload(CommonsMultipartFile firstFile, CommonsMultipartFile secondFile, HttpServletRequest request,
        HttpServletResponse response) throws IOException
    {
        response.setContentType("text/html");
        if(firstFile.getSize() == 0 || secondFile.getSize() == 0)
        {
            response.getWriter().print(AppConstants.UPLOAD_ZERO_SIZE_FILE);
            return;
        }

        // other validations
        // uploading:
        try
        {
            String content = request.getParameter(CoupleUploadPanel.CONTENT);
            T model = deserialize(content);
            UploadResultModel resultModel = upload(model,firstFile,secondFile); // it's implemented in UploadFileServletImpl 
            if(resultModel.hasCriticalError())
            {
                response.getWriter().print(AppConstants.UPLOAD_FAIL + "," + String.valueOf(resultModel.getWorkshopHistoryId()));
            }
            else
            {
                response.getWriter().print(AppConstants.UPLOAD_SUCCESS + "," + String.valueOf(resultModel.getWorkshopHistoryId()));
            }
        }
        catch(ProcessRequestException e)
        {
           // write upload error description in response.getWriter()
        }
        catch(Exception e)
        {
            e.printStackTrace();
            response.getWriter().print(AppConstants.UPLOAD_UNKOWN_ERROR);
        }
    }

    ...
}

I have a multipartResolver bean in my app-servlet.xml (file.upload.max_size=9437184), And also a maxUploadSizeExceededExceptionHandler bean for handling UploadSizeExceededExceptions:

 <bean id="multipartResolver" class="org.springframework.web.multipart.commons.CommonsMultipartResolver">
     <property name="maxUploadSize" value="${file.upload.max_size}" />
 </bean>
 <bean id="maxUploadSizeExceededExceptionHandler" class="com.insurance.ui.server.uploadfile.MaxUploadSizeExceededExceptionHandler">
     <property name="order" value="1"/>
 </bean>

My maxUploadSizeExceededExceptionHandler:

public class MaxUploadSizeExceededExceptionHandler implements HandlerExceptionResolver, Ordered
{
    private int order;

    @Override
    public ModelAndView resolveException(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex)
    {
        if(ex instanceof MaxUploadSizeExceededException)
        {
            try
            {
                response.getWriter().print(ErrorConstants.UPLOAD_SIZE_EXCEED + "," + (((MaxUploadSizeExceededException) ex).getMaxUploadSize()/(1024*1024)));
                response.getWriter().flush();
                response.flushBuffer();
                return new ModelAndView();
            }
            catch(IOException e)
            {
            }
        }
        return null;
    }
    ...
}

When i upload a very large file (more than ${file.upload.max_size}, about 700MB), CommonsMultipartResolver throws MaxUploadSizeExceededException immediately which I'm catching and handling it (writing in response.getWriter()) .But My problem: My browser upload-progress bar shows the file is still uploading!! Why?

UPDATE: I'm using:

  • Spring-*-3.0.5.RELEASE
  • commons-fileupload-1.1.1

and also tried:

  • Spring-*-3.1.2.RELEASE
  • commons-fileupload-1.3

and my AS:

  • Tomcat 6 (in development)
  • Jboss 7 (in production)

UPDATE 2: In the client side, i'm using GWT (I think it doesn't matter):

Uploading is started by clicking submitRequestButton:

@UiHandler("submitRequestButton")
public void submitRequestButtonClick(ClickEvent event)
{
    try
    {
        // some validation
        submitRequestButton.setEnabled(false);
        uploadPanel.upload(model.getWorkshopHistoryModel()); // uploadPanel is from the CoupleUploadPanel type
    }
    catch(ValidationException exception)
    {
        // handle validation errors
    }
    catch(SerializationException e)
    {
        // handle serialization errors
    }
}

I have a CoupleUploadPanel Widget for uploading (two files):

public class CoupleUploadPanel<T extends Serializable> extends FormPanel
{
    public final static String CONTENT = "content";
    private static final String FIRST_FILE = "firstFile";
    private static final String SECOND_FILE = "secondFile";

    private Hidden contentInput;
    private FileUpload firstFileUploadInput;
    private FileUpload secondFileUploadInput;
    private SerializationStreamFactory factory;

    public CoupleUploadPanel(UploadServletAsync<T> factory)
    {
        this(null,factory);
    }

    public CoupleUploadPanel(String url, UploadServletAsync<T> factory)
    {
        this.factory = (SerializationStreamFactory) factory;
        if(url != null)
        {
            setAction(url);
        }
        init();
    }
    public CoupleUploadPanel(String target, String url, UploadServletAsync<T> factory)
    {
        super(target);
        this.factory = (SerializationStreamFactory) factory;
        if(url != null)
        {
            setAction(url);
        }
        init();
    }

    private void init()
    {
        setMethod("POST");
        setEncoding(ENCODING_MULTIPART);
        firstFileUploadInput = new FileUpload();
        firstFileUploadInput.setName(CoupleUploadPanel.FIRST_FILE);
        secondFileUploadInput = new FileUpload();
        secondFileUploadInput.setName(CoupleUploadPanel.SECOND_FILE);
        contentInput = new Hidden();
        contentInput.setName(CONTENT);
        VerticalPanel panel = new VerticalPanel();
        panel.add(firstFileUploadInput);
        panel.add(secondFileUploadInput);
        panel.add(contentInput);
        add(panel);
    }

    public void upload(T input) throws SerializationException
    {
        contentInput.setValue(serialize(input));
        submit();
    }

    private String serialize(T input) throws SerializationException
    {
        SerializationStreamWriter writer = factory.createStreamWriter();
        writer.writeObject(input);
        return writer.toString();
    }
}

We should pass a UploadServletAsync to CoupleUploadPanel constructor. UploadServletAsync and UploadServlet interfaces:

public interface UploadServletAsync<T extends Serializable> 
{
    void upload(T model, AsyncCallback<Void> callback);
}

public interface UploadServlet<T extends Serializable> extends RemoteService
{
    void upload(T model);
}

So the uploadPanel will be instantiated in this way:

uploadPanel= new CoupleUploadPanel<WorkshopHistoryModel>((UploadFileServletAsync) GWT.create(UploadFileServlet.class));
uploadPanel.setAction(UploadFileServlet.URL);

And a SubmitCompeleteHandler added to uploadPanel (onSumbitComplete() will be called when submit completed and results passed to the client side):

uploadPanel.addSubmitCompleteHandler(new SubmitCompleteHandler()
{

    @Override
    public void onSubmitComplete(SubmitCompleteEvent event)
    {
        String s = event.getResults(); //contains whatever written by response.getWriter() 
        if(s == null)
        {
            // navigate to request list page
        }
        else
        {
            String[] response = s.split(",");
            // based on response: 
            // show error messages if any error occurred in file upload
            // else: navigate to upload result page
        }
    }
});

UploadFileServlet and UploadFileServletAsync interfaces:

public interface UploadFileServlet extends UploadServlet<WorkshopHistoryModel>
{
    String URL = "**/uploadFileService.mvc";
}

public interface UploadFileServletAsync extends UploadServletAsync<WorkshopHistoryModel>
{
    public static final UploadFileServletAsync INSTANCE = GWT.create(UploadFileServlet.class);
}

In the server side: the UploadFileServletImpl extends AbstractCoupleUploadController and implements the upload() method (upload process):

@RequestMapping(UploadFileServlet.URL)
public class UploadFileServletImpl extends AbstractCoupleUploadController<WorkshopHistoryModel>
{
    ...

    @Override
    protected UploadResultModel upload(WorkshopHistoryModel model, MultipartFile firstFile, MultipartFile secondFile)
            throws ProcessRequestException
    {
        return workshopHistoryService.submitList(model.getWorkshop(),firstFile,secondFile);
    }

    ...
}
Mordacious answered 31/10, 2013 at 22:7 Comment(4)
Have you tried using response.sendError(500) in resolveException? As of now you only write the message to the response without saying it's an error. Also show your code you using for uploading the file.Burnet
@Burnet I added the client side code (i'm using GWT), but i think it doesn't matterPsychosomatics
Off topic : If it isn't a pain then, upgrade Spring to 3.2.x and Tomcat to 7.x.x to get rid of troubles in future.Ranking
I am facing same problem. Any solution ?Lupine
C
1

Well, afaik Spring (a servlet and some filters) does not observe the uploading process, but only handle the result of the finished process. That's because the upload is processed by Tomcat itself (hint: there is an upload size limit option in the web.xml). So it can be possible to make an upload failure (which Spring would not be noticed) or to upload a file which is too large. And only when 2nd happend, the specific filter/interceptor could deny the process.

In my last setup, I had used Nginx as proxy in front of a Tomcat:

  1. If your browser sends the actual file size (modern browsers do, at least IE7? or IE8? does not), than Nginx will send a 500 if the size exceeds the defined limit.
  2. I'm not 100% sure: If the uploaded size exceeds the specified limit, Nginx will send a 500 too. That would cancel the underlaying connection to the Tomcat, too.
Cresida answered 31/10, 2013 at 22:7 Comment(0)
M
0
yourfile.getFile().getSize() > Long.parseLong(153600);

This code will approve to upload a file less than 150 kb. If it exceeds 150 kb you can send any error msg.

Merge answered 31/10, 2013 at 22:7 Comment(0)
Y
0

As a first try I would call response.sendError(HttpServletResponse.SC_INTERNAL_SERVER_ERROR) in MaxUploadSizeExceededExceptionHandler.

Then I would check one or two SO questions to see whether they would contain some helpful information I could try out.

If this doesn't help I would investigate the sources of GwtUpload and see how they implemented it (or just start using their implementation).

You answered 31/10, 2013 at 22:7 Comment(0)
D
0

We use the following method:

public class MultipartResolver extends CommonsMultipartResolver {

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);
    }
}

public void cleanupMultipart(MultipartHttpServletRequest request) {
    super.cleanupMultipart(request);
}    

public void setFileSizeMax(long fileSizeMax) {
    getFileUpload().setSizeMax(-1);
    getFileUpload().setFileSizeMax(fileSizeMax);
}

}

Dimaggio answered 31/10, 2013 at 22:7 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.