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