Обработка MaxUploadSizeExceededException не может остановить загрузку файла

Я хочу проверить размер загрузки файлов и полностью заблокировать файлы, загруженные в память. Я использую CommonsMultipartFile. Загруженный файл будет обработан и сохранен в БД. Класс AbstractCoupleUploadController обрабатывает входящий запрос, содержащий файлы:

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

    ...
}

У меня есть multipartResolver bean в моем app-servlet.xml(file.upload.max_size = 9437184), а также maxUploadSizeExceededExceptionHandler bean для обработки 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;
    }
    ...
}

Когда я загружаю очень большой файл (более ${file.upload.max_size}, около 700 МБ), CommonsMultipartResolver сразу бросает MaxUploadSizeExceededException, которое я улавливаю и обрабатываю (записывая в response.getWriter()). Но Моя проблема: в моей строке загрузки загрузки браузера показано, что файл по-прежнему загружается! Почему?

ОБНОВЛЕНИЕ: Я использую:

  • Spring - * - 3.0.5.RELEASE
  • Обще-FileUpload-1.1.1

а также попытался:

  • Spring - * - 3.1.2.RELEASE
  • Обще-FileUpload-1,3

и мой AS:

  • Tomcat 6 (в разработке)
  • Jboss 7 (на производстве)

ОБНОВЛЕНИЕ 2: На стороне клиента я использую GWT (я думаю, это не имеет значения):

Загрузка начинается с нажатия кнопки 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
    }
}

У меня есть Виджет пары для подключения (два файла):

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

Мы должны передать конструктор UploadServletAsync в конструктор CoupleUploadPanel. Интерфейсы UploadServletAsync и UploadServlet:

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

Таким образом, uploadPanel будет создан таким образом:

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

И добавление SubmitCompeleteHandler в uploadPanel (onSumbitComplete() будет вызываться, когда отправка завершена, а результаты передаются на клиентскую сторону):

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 и UploadFileServletAsync:

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

На стороне сервера: UploadFileServletImpl расширяет AbstractCoupleUploadController и реализует метод upload() (процесс загрузки):

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

    ...
}

Ответ 1

Хорошо, afaik Spring (сервлет и некоторые фильтры) не отслеживает процесс загрузки, а обрабатывает результат только законченного процесса. Это потому, что загрузка обрабатывается самим Tomcat (подсказка: есть опция ограничения размера загрузки в web.xml). Таким образом, может произойти сбой при загрузке (который не будет замечен Spring) или загрузить слишком большой файл. И только когда 2-й случай, конкретный фильтр/перехватчик может отказать в этом процессе.

В моей последней настройке я использовал Nginx в качестве прокси-сервера перед Tomcat:

  • Если ваш браузер отправляет фактический размер файла (современные браузеры делают, по крайней мере, IE7 или IE8?), то Nginx отправит 500, если размер превышает указанный предел.
  • Я не уверен на 100%: если загруженный размер превышает указанный предел, Nginx также отправит 500. Это также отменит подстилающее соединение с Tomcat.

Ответ 2

Мы используем следующий метод:

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

}

Ответ 3

В качестве первой попытки я бы назвал response.sendError(HttpServletResponse.SC_INTERNAL_SERVER_ERROR) в MaxUploadSizeExceededExceptionHandler.

Затем я бы проверил один или два вопроса, чтобы узнать, будут ли они содержать какую-то полезную информацию. я может попробовать.

Если это не поможет, я бы исследовал источники GwtUpload и посмотрел, как они их реализовали (или просто начинают использовать их реализация).

Ответ 4

yourfile.getFile().getSize() > Long.parseLong(153600);

Этот код будет одобрен для загрузки файла размером менее 150 кб. Если он превышает 150 кб, вы можете отправить любую ошибку. Msg.