org.hd.d.pg2k.webSvr.upload
Class HTTPUploaderUtils

java.lang.Object
  extended by org.hd.d.pg2k.webSvr.upload.HTTPUploaderUtils

public final class HTTPUploaderUtils
extends java.lang.Object

Code snippets to support exhibit upload. Some of these are here just to keep the JSP files shorter, simpler, clearer, and faster to compile.

Thanks to Benoît Marchal for example code to handle multipart/form-data HTTP POST data.


Field Summary
private static int DU_COLLECTING_FILE
          doUpload() collecting file.
private static int DU_COLLECTING_PARAM
          doUpload() collecting parameter value.
private static int DU_GOT_FILE_NAME
          doUpload() got filename.
private static int DU_GOT_PARAM_NAME
          doUpload() got parameter name.
private static int DU_INITIAL_STATE
          doUpload() initial state.
static int MIN_BATCH_UPLOAD_SPACE
          Minimum space that must be left before we will allow an upload; strictly positive.
private static int RW_BLOCK_SIZE
          Chunk size for reads/writes in upload; bigger is probably more efficient.
private static java.io.File transcriptName
          If not null, relative pathname used to record transcript of exhibit upload.
 
Constructor Summary
private HTTPUploaderUtils()
          Prevent instantiation.
 
Method Summary
static boolean doLogout(java.net.URL loginURL)
          Logout from the upload server if possible.
static void doUpload(javax.servlet.http.HttpServletRequest request, UploadInfoBean uib, int maxUploadFileSize, java.io.File destinationDir, SimpleLoggerIF logger)
          Accepts the body of a multipart/form-data POST request.
static void doUpload(UploadInfoBean uib, java.io.File destinationDir, javax.servlet.ServletInputStream is, java.lang.String boundary, int maxUploadFileSize, SimpleLoggerIF logger)
          Accepts the body of a multipart/form-data POST request.
static boolean doUploadLogin(boolean justPoll, java.net.URL loginURL, java.lang.String userID, java.lang.String passS)
          Attempts to programmatically login at the given login URL with the user ID and password supplied.
static void generateUploadPOSTBody(UploadInfoBean uib, java.io.OutputStream os, byte[] data, java.lang.String boundary)
          Writes the POST body for a multi-part MIME stream to upload an exhibit.
static java.lang.String isLoggedInForUploads(javax.servlet.http.HttpServletRequest request)
          Returns ID if user is logged in for uploaded, else null.
static void runBatchUploadProtocolServerSide(SimpleLoggerIF logger, AllExhibitProperties aep, GenProps gp, javax.servlet.ServletInputStream rawIS, javax.servlet.ServletOutputStream rawOS, java.lang.String uploadDirS, java.lang.String userID, java.lang.String userPass)
          Run the server side of the batch upload protocol, after authentication.
private static javax.servlet.ServletInputStream transcribeInputStream(javax.servlet.ServletInputStream is)
          Take a transcript of the input stream.
 
Methods inherited from class java.lang.Object
clone, equals, finalize, getClass, hashCode, notify, notifyAll, toString, wait, wait, wait
 

Field Detail

transcriptName

private static final java.io.File transcriptName
If not null, relative pathname used to record transcript of exhibit upload. Generally only used for debugging.

Multiple uploads' transcripts will overwrite/corrupt one another. This is not intended for anything other than helping with testing.

Is null by default for release builds.


DU_INITIAL_STATE

private static final int DU_INITIAL_STATE
doUpload() initial state.

See Also:
Constant Field Values

DU_GOT_FILE_NAME

private static final int DU_GOT_FILE_NAME
doUpload() got filename.

See Also:
Constant Field Values

DU_GOT_PARAM_NAME

private static final int DU_GOT_PARAM_NAME
doUpload() got parameter name.

See Also:
Constant Field Values

DU_COLLECTING_FILE

private static final int DU_COLLECTING_FILE
doUpload() collecting file.

See Also:
Constant Field Values

DU_COLLECTING_PARAM

private static final int DU_COLLECTING_PARAM
doUpload() collecting parameter value.

See Also:
Constant Field Values

RW_BLOCK_SIZE

private static final int RW_BLOCK_SIZE
Chunk size for reads/writes in upload; bigger is probably more efficient. Ideally (much) bigger than maximum magic number size so that we can read that from the first uploaded block.

Should probably be at least 512 to exceed minimum guaranteed Internet MTU and disc sector size, and probably a power of 2.

A good value is probably from 512 to 65536;

See Also:
Constant Field Values

MIN_BATCH_UPLOAD_SPACE

public static final int MIN_BATCH_UPLOAD_SPACE
Minimum space that must be left before we will allow an upload; strictly positive. Should be enough for an average-to-large typical exhibit, eg several MB.

See Also:
Constant Field Values
Constructor Detail

HTTPUploaderUtils

private HTTPUploaderUtils()
Prevent instantiation.

Method Detail

isLoggedInForUploads

public static final java.lang.String isLoggedInForUploads(javax.servlet.http.HttpServletRequest request)
Returns ID if user is logged in for uploaded, else null. Can only be logged in if there is a session in place.

Parameters:
request - current HTTP request; never null

doUpload

public static void doUpload(javax.servlet.http.HttpServletRequest request,
                            UploadInfoBean uib,
                            int maxUploadFileSize,
                            java.io.File destinationDir,
                            SimpleLoggerIF logger)
                     throws java.io.IOException,
                            java.io.InvalidObjectException
Accepts the body of a multipart/form-data POST request. This does not handle lots of complicated possible cases from complicated forms or strange/broken browsers.

In particular this only expects one file upload at a time.

This streams the data more-or-less directly to disc, so that huge uploads can be handled with fixed, modest memory.

This will verify the magic number as the file is uploaded, and abort the upload if it is too long.

The upload is to a temporary file beside (in the same directory as) the final destination (to avoid problems moving across filesystems) and is moved into place atomically if the upload is successful, and removed if unsuccessful. (Any that are left in place after a crash are easily identifiable for manual removal.)

This puts the uploaded file below destinationDir, creating any subdirectories needed, in a path of the form categoryDir/name eg art/cat-1-ANON.jpg.

This will not allow overwrite any existing file as a security precaution.

This does not serialise access to the upload area; this must be done externally if required.

We rely on UploadInfoBean only creating sensible and safe pathnames.

FIXME: we should probably validate the uploaded file content by trying whatever tests we have to hand before finally accepting it.

Parameters:
request - the HTTP POST request
destinationDir - the root directory to upload to
maxUploadFileSize - the maximum space that the uploaded file may consume (bytes); must be positive
uib - information on the bean to upload, must be non-null and be able to generate a valid unique name
Throws:
java.io.IOException - in case of I/O problems
java.io.InvalidObjectException - if the upload has a bad magic number

doUpload

public static void doUpload(UploadInfoBean uib,
                            java.io.File destinationDir,
                            javax.servlet.ServletInputStream is,
                            java.lang.String boundary,
                            int maxUploadFileSize,
                            SimpleLoggerIF logger)
                     throws java.io.IOException,
                            java.io.InvalidObjectException
Accepts the body of a multipart/form-data POST request. This does not handle lots of complicated possible cases from complicated forms or strange/broken browsers.

In particular this only expects one file upload at a time.

This streams the data more-or-less directly to disc, so that huge uploads can be handled with fixed, modest memory.

This will verify the magic number as the file is uploaded, and abort the upload if it is too long.

The upload is to a temporary file beside (in the same directory as) the final destination (to avoid problems moving across filesystems) and is moved into place atomically if the upload is successful, and removed if unsuccessful. (Any that are left in place after a crash are easily identifiable for manual removal.)

This puts the uploaded file below destinationDir, creating any subdirectories needed, in a path of the form categoryDir/name eg art/cat-1-ANON.jpg.

This will not allow overwrite any existing file as a security precaution.

This does not serialise access to the upload area; this must be done externally if required.

We rely on UploadInfoBean only creating sensible and safe pathnames.

FIXME: we should probably validate the uploaded file content by trying whatever tests we have to hand before finally accepting it.

Parameters:
is - the input stream from the HTTP POST request
destinationDir - the root directory to upload to
maxUploadFileSize - the maximum space that the uploaded file may consume (bytes); must be positive
uib - information on the bean to upload, must be non-null and be able to generate a valid unique name
boundary - the MIME multi-part boundary string (prefixed with lots of "-"s, exactly as will start a line)
Throws:
java.io.IOException - in case of I/O problems
java.io.InvalidObjectException - if the upload has a bad magic number

generateUploadPOSTBody

public static final void generateUploadPOSTBody(UploadInfoBean uib,
                                                java.io.OutputStream os,
                                                byte[] data,
                                                java.lang.String boundary)
                                         throws java.io.IOException
Writes the POST body for a multi-part MIME stream to upload an exhibit. This assumes that the other end has already been conveyed the content of the UploadInfoBean, and that the appropriate headers will be set for the body.

The non-null, non-empty, unique, not-appearing-in-the-data boundary String must be supplied.

We insist that the boundary marker is pure printable ASCII and at least 8 characters; it should usually be much longer. This should have a random component for safety.

The OutputStream should be buffered if at all possible, for efficiency.

Parameters:
uib - description of the the exhibit; never null
os - stream to write body to; never null
data - raw exhibit to upload, not altered by the routine; never null
boundary - unique boundary string; never empty or null
Throws:
java.io.IOException

transcribeInputStream

private static javax.servlet.ServletInputStream transcribeInputStream(javax.servlet.ServletInputStream is)
                                                               throws java.io.FileNotFoundException
Take a transcript of the input stream. The transcriptName must be non-null (else no transcript will be made) and it must be a file that we can open write.

Parameters:
is - input stream to be transcribed
Throws:
java.io.FileNotFoundException

doUploadLogin

public static boolean doUploadLogin(boolean justPoll,
                                    java.net.URL loginURL,
                                    java.lang.String userID,
                                    java.lang.String passS)
                             throws java.io.IOException
Attempts to programmatically login at the given login URL with the user ID and password supplied. Assumed that any cookies/session required will automatically be held onto by the JVM.

Parameters:
justPoll - see if we remain logged in from previous attempt
loginURL - non-null URL (within our codebase)
userID - valid userID; never null
passS - valid password; never null
Returns:
true if successful, false if not
Throws:
java.io.IOException - in case of I/O problems

doLogout

public static boolean doLogout(java.net.URL loginURL)
                        throws java.io.IOException
Logout from the upload server if possible.

Parameters:
loginURL - non-null URL (within our codebase)
Returns:
true if successful, false if not
Throws:
java.io.IOException - in case of I/O problems

runBatchUploadProtocolServerSide

public static void runBatchUploadProtocolServerSide(SimpleLoggerIF logger,
                                                    AllExhibitProperties aep,
                                                    GenProps gp,
                                                    javax.servlet.ServletInputStream rawIS,
                                                    javax.servlet.ServletOutputStream rawOS,
                                                    java.lang.String uploadDirS,
                                                    java.lang.String userID,
                                                    java.lang.String userPass)
                                             throws java.io.IOException
Run the server side of the batch upload protocol, after authentication. Can be called directly from servlet doPOST() method.

(Can also be used stand-alone for unit testing.)

This waits for a request from the client, then sends a FAIL response in case of immediate trouble, or collects the uploaded file, then sends an OK or FAIL response as appropriate.

Parameters:
rawIS - input stream from client; never null
rawOS - output stream to client; never null
uploadDirS - upload directory; valid and never null
userID - user ID authenticated with; valid syntax and never null
userPass - password authenticated with; valid syntax and never null
Throws:
java.io.IOException - in case of error

DHD Multimedia Gallery V1.57.21

Copyright (c) 1996-2011, Damon Hart-Davis. All rights reserved.