blob: fb234cac713ee57be9f5c447b8c0115759448884 [file] [log] [blame]
/*******************************************************************************
* Copyright (c) 2011, 2012 EclipseSource and others.
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*
* Contributors:
* EclipseSource - initial API and implementation
******************************************************************************/
package org.eclipse.rap.rwt.supplemental.fileupload.internal;
import java.io.IOException;
import java.util.List;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.apache.commons.fileupload.FileUploadBase.FileSizeLimitExceededException;
import org.apache.commons.fileupload.FileUploadException;
import org.apache.commons.fileupload.ProgressListener;
import org.apache.commons.fileupload.disk.DiskFileItem;
import org.apache.commons.fileupload.disk.DiskFileItemFactory;
import org.apache.commons.fileupload.servlet.ServletFileUpload;
import org.apache.commons.io.FileCleaningTracker;
import org.eclipse.rap.rwt.supplemental.fileupload.FileUploadHandler;
import org.eclipse.rap.rwt.supplemental.fileupload.FileUploadReceiver;
import org.eclipse.rap.rwt.supplemental.fileupload.IFileUploadDetails;
import org.eclipse.rwt.RWT;
import org.eclipse.rwt.service.SessionStoreEvent;
import org.eclipse.rwt.service.SessionStoreListener;
final class FileUploadProcessor {
private static final String CLEANING_TRACKER_ATTR_NAME
= FileCleaningTracker.class.getName() + ".fileupload";
private final FileUploadHandler handler;
private final FileUploadTracker tracker;
FileUploadProcessor( FileUploadHandler handler ) {
this.handler = handler;
tracker = new FileUploadTracker( handler );
}
void handleFileUpload( HttpServletRequest request, HttpServletResponse response )
throws IOException
{
try {
DiskFileItem fileItem = readUploadedFileItem( request );
if( fileItem != null ) {
String fileName = stripFileName( fileItem.getName() );
String contentType = fileItem.getContentType();
long contentLength = fileItem.getSize();
tracker.setFileName( fileName );
tracker.setContentType( contentType );
FileUploadReceiver receiver = handler.getReceiver();
IFileUploadDetails details = new FileUploadDetails( fileName, contentType, contentLength );
receiver.receive( fileItem.getInputStream(), details );
tracker.handleFinished();
} else {
String errorMessage = "No file upload data found in request";
tracker.setException( new Exception( errorMessage ) );
tracker.handleFailed();
response.sendError( HttpServletResponse.SC_BAD_REQUEST, errorMessage );
}
} catch( FileSizeLimitExceededException exception ) {
// Note: Apache fileupload 1.2 will throw an exception after the upload is finished.
// Therefore we handle it in the progress listener and ignore this kind of exceptions here
// https://issues.apache.org/jira/browse/FILEUPLOAD-145
response.sendError( HttpServletResponse.SC_REQUEST_ENTITY_TOO_LARGE, exception.getMessage() );
} catch( Exception exception ) {
tracker.setException( exception );
tracker.handleFailed();
response.sendError( HttpServletResponse.SC_INTERNAL_SERVER_ERROR, exception.getMessage() );
}
}
private DiskFileItem readUploadedFileItem( HttpServletRequest request )
throws FileUploadException
{
ServletFileUpload upload = createUpload();
DiskFileItem result = null;
List uploadedItems = upload.parseRequest( request );
// TODO [rst] Support multiple fields in multipart message
if( uploadedItems.size() > 0 ) {
// TODO [rst] Upload fails if the file is not the first field
DiskFileItem fileItem = ( DiskFileItem )uploadedItems.get( 0 );
// Don't check for file size == 0 because this would prevent uploading empty files
if( !fileItem.isFormField() ) {
result = fileItem;
}
}
return result;
}
private ServletFileUpload createUpload() {
DiskFileItemFactory factory = new DiskFileItemFactory();
factory.setFileCleaningTracker( getCleaningTracker() );
ServletFileUpload result = new ServletFileUpload( factory );
long maxFileSize = getMaxFileSize();
result.setFileSizeMax( maxFileSize );
ProgressListener listener = createProgressListener( maxFileSize );
result.setProgressListener( listener );
return result;
}
private ProgressListener createProgressListener( final long maxFileSize ) {
ProgressListener result = new ProgressListener() {
long prevTotalBytesRead = -1;
public void update( long totalBytesRead, long contentLength, int item ) {
// Depending on the servlet engine and other environmental factors,
// this listener may be notified for every network packet, so don't notify unless there
// is an actual increase.
if ( totalBytesRead > prevTotalBytesRead ) {
prevTotalBytesRead = totalBytesRead;
// Note: Apache fileupload 1.2.x will throw an exception after the upload is finished.
// So we handle the file size violation as best we can from here.
// https://issues.apache.org/jira/browse/FILEUPLOAD-145
if( maxFileSize != -1 && contentLength > maxFileSize ) {
tracker.setException( new Exception( "File exceeds maximum allowed size." ) );
tracker.handleFailed();
} else {
tracker.setContentLength( contentLength );
tracker.setBytesRead( totalBytesRead );
tracker.handleProgress();
}
}
}
};
return result;
}
private long getMaxFileSize() {
return handler.getMaxFileSize();
}
private static String stripFileName( String name ) {
String result = name;
int lastSlash = result.lastIndexOf( '/' );
if( lastSlash != -1 ) {
result = result.substring( lastSlash + 1 );
} else {
int lastBackslash = result.lastIndexOf( '\\' );
if( lastBackslash != -1 ) {
result = result.substring( lastBackslash + 1 );
}
}
return result;
}
private FileCleaningTracker getCleaningTracker() {
FileCleaningTracker cleaningTracker
= ( FileCleaningTracker )RWT.getSessionStore().getAttribute( CLEANING_TRACKER_ATTR_NAME );
if ( cleaningTracker == null ) {
// Create cleaning tracker for current session and register session listener that will
// destroy it
cleaningTracker = new FileCleaningTracker();
RWT.getSessionStore().setAttribute( CLEANING_TRACKER_ATTR_NAME, cleaningTracker );
RWT.getSessionStore().addSessionStoreListener( new FileUploadCleanupHandler() );
}
return cleaningTracker;
}
private static class FileUploadCleanupHandler implements SessionStoreListener {
public void beforeDestroy( SessionStoreEvent event ) {
// Destroy the cleaning tracker
FileCleaningTracker cleaningTracker
= ( FileCleaningTracker ) RWT.getSessionStore().getAttribute( CLEANING_TRACKER_ATTR_NAME );
RWT.getSessionStore().removeAttribute( CLEANING_TRACKER_ATTR_NAME );
cleaningTracker.exitWhenFinished();
}
}
}