Java tutorial
// Copyright (C) 2007-2012 Bristle Software, Inc. // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation; either version 1, or (at your option) // any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License // along with this program; if not, write to the Free Software // Foundation, Inc. package com.bristle.javalib.net.http; import java.io.IOException; import java.io.InputStream; import java.util.HashMap; import javax.servlet.http.HttpServletRequest; import org.apache.commons.fileupload.FileItemIterator; import org.apache.commons.fileupload.FileItemStream; import org.apache.commons.fileupload.FileItemStream.ItemSkippedException; import org.apache.commons.fileupload.FileUploadException; import org.apache.commons.fileupload.servlet.ServletFileUpload; import org.apache.commons.fileupload.util.Streams; // MultiPartFormDataParamMap /****************************************************************************** * This class makes the form parameters from an HTML form with * enctype='multipart/form-data' * available as a simple map, like the map returned by * {@link HttpServletRequest#getParameterMap()} * for other HTML forms. It can also be used with regular HTML forms, but it * offers no advantages in that case, because it simply returns the map * returned by {@link HttpServletRequest#getParameterMap()}. * <br> * It handles regular form parameters differently than streamed file * parameters (the kind generated by the HTML INPUT tag with TYPE='FILE'). * Regular form parameters are added to the map, and can be accessed at any * time, in the usual fashion. However, streamed files are not added to * the map. Instead, they are passed as {@link FileItemStream}s to the * specified callback method (if any) as they are discovered during the * iteration over the incoming HTTP request stream. Such files must be fully * processed during the callback, because they become unavailable as soon as * the iteration resumes. This is because they are really just part of the * larger HTTP request stream. Attempts to access the {@link FileItemStream} * after the callback completes throw the exception: * {@link ItemSkippedException} * *<pre> *<b>Usage:</b> * * - The typical scenario for using this class from Java code is: * - Call {@link #parseRequestStream(HttpServletRequest,FileItemStreamCallBack)} * or {@link #parseRequestStream(HttpServletRequest)}, passing an HTTP * request and an optional callback. This parses the request, and calls * the callback (if any) for each file (if any) in the streamed HTTP * request: * import org.apache.commons.fileupload.FileItemStream; * class MyCallback * implements MultiPartFormDataParamMap.FileItemStreamCallBack * { * public void fullyProcessAFileItemStream * (FileItemStream fileItemStream) * throws Exception * { * String strFieldName = fileItemStream.getFieldName()); * String strFileName = fileItemStream.getName(); * String strContentType = fileItemStream.getContentType(); * InputStream streamIn = fileItemStream.openStream(); * processTheStreamSavingToDiskOrWhatever(streamIn); * } * } * ... * MultiPartFormDataParamMap formParams * = new MultiPartFormDataParamMap(); * formParams.parseRequestStream(request, new MyCallback()); * - Access the param map as you would ordinarily access the map returned * by {@link HttpServletRequest#getParameterMap()}: * formParams.containsKey("param1") * formParams.containsKey(strParam) * formParams.get("param1") * formParams.get(strParam) * etc. * - For form parameters that occur earlier in the HTTP request stream * than a file, you can access the formParams map during the callback * for that file as well as after it. For form parameters that occur * later in the stream, the parameters will not yet be in the map. * * - The typical scenario for using this class from JSP code is: * - Call {@link #parseRequestStream(HttpServletRequest,FileItemStreamCallBack)} * or {@link #parseRequestStream(HttpServletRequest)} from Java code, * exactly as above. * - Add the map to the context for convenient access from EL expressions: * <% * pageContext.setAttribute("formParams", formParams); * %> * - Access the non-file params in the formParams map via EL: * ${formParams.param1} * ${formParams["param1"]} * ${formParams[strParam]} * *<b>Assumptions:</b> *<b>Effects:</b> * - None. *<b>Anticipated Changes:</b> *<b>Notes:</b> *<b>Implementation Notes:</b> *<b>Portability Issues:</b> *<b>Revision History:</b> * $Log$ *</pre> ******************************************************************************/ public class MultiPartFormDataParamMap extends HashMap { // // Class variables // // // Instance variables to support public properties // // // Internal instance variables // /************************************************************************** * This number identifies the version of the class definition, used for * serialized instances. Be sure to increment it when adding/modifying * instance variable definitions or making any other change to the class * definition. Omitting this declaration causes a compiler warning for * any class that implements java.io.Serializable. *************************************************************************/ private static final long serialVersionUID = 1L; /************************************************************************** * This interface must be implemented by any class that expects to be * called as a callback by {@link #parseRequestStream(HttpServletRequest, * FileItemStreamCallBack)}. **************************************************************************/ public interface FileItemStreamCallBack { /********************************************************************** * Fully process the specified FileItemStream, without keeping a * reference to it. *@param fileItemStream The FileItemStream to fully process. *@throws Throwable Permitted to throw any Throwable. * It will be propagated to the caller of * {@link #parseRequestStream(HttpServletRequest, * FileItemStreamCallBack)}. **********************************************************************/ public void fullyProcessAFileItemStream(FileItemStream fileItemStream) throws Throwable; } /************************************************************************** * Parse the specified HTTP request, initializing the map, and calling * the specified callback (if not null) for each file (if any) in the * streamed HTTP request. * *@param request The HTTP request *@param callback The callback class *@throws FileUploadException When the request is badly formed. *@throws IOException When an I/O error occurs reading the request. *@throws Throwable When thrown by the callback. **************************************************************************/ public void parseRequestStream(HttpServletRequest request, FileItemStreamCallBack callback) throws FileUploadException, IOException, Throwable { if (ServletFileUpload.isMultipartContent(request)) { ServletFileUpload upload = new ServletFileUpload(); FileItemIterator iter = upload.getItemIterator(request); while (iter.hasNext()) { FileItemStream fileItemStream = iter.next(); if (fileItemStream.isFormField()) { String strParamName = fileItemStream.getFieldName(); InputStream streamIn = fileItemStream.openStream(); String strParamValue = Streams.asString(streamIn); put(strParamName, strParamValue); // Note: Can't do the following usefully. The Parameter // Map of the HTTP Request is effectively readonly. // This does not report an error, but is a no-op. // request.getParameterMap().put(strParamName, strParamValue); } else { if (callback != null) { callback.fullyProcessAFileItemStream(fileItemStream); } } } } else { putAll(request.getParameterMap()); } } /************************************************************************** * Parse the specified HTTP request, initializing the map. * *@param request The HTTP request *@throws FileUploadException When the request is badly formed. *@throws IOException When an I/O error occurs reading the request. **************************************************************************/ public void parseRequestStream(HttpServletRequest request) throws FileUploadException, IOException { try { parseRequestStream(request, null); } catch (FileUploadException exception) { throw exception; } catch (IOException exception) { throw exception; } catch (Throwable exception) { // Catch Throwable here to avoid having to declare it in the // the throws clause. It is declared in: // parseRequestStream(request, callback) // because the callback may throw it, and we want to propagate // it to the caller. But, in this case, there is no callback, // so it is hard to justify declaring Throwable in the throws // clause. //?? Not ideal because now we are catching Throwable, in the //?? unlikely case that an undeclared exception occurs. How to //?? not catch it, but not have to declare it? } } }