Package org.bzdev.net

Class FormDataIterator

java.lang.Object
org.bzdev.net.FormDataIterator
All Implemented Interfaces:
Iterator<InputStream>

public class FormDataIterator extends Object implements Iterator<InputStream>
Iterator to read input streams that provide data using the multipart/form-data media type.

The embedded HTTP server handles POST requests by providing an input stream containing some data. HTML forms in particular will generate such a stream using the multipart/form-data media type. When implementing a subclass of WebMap, given an instance of WebMap.RequestInfo, requestInfo, one will typically use the following design pattern:


     InputStream is = requestInfo.getDecodedInputStream();
     String mediatype = requestInfo.getFromHeader("content-type", null);
     if (mediatype.equalsIgnoreCase("multipart/form-data")) {
         String boundary =
              requestHeader.getFromHeader("content-type", "boundary");
         FormDataInterator it = new FormDataIterator(is, boundary);
         while (it.hasNext()) {
            InputStream cis = it.next();
            ...
            cis.close();
         }
     }
 
If the content-type header's value CTYPE, and the input stream is containing the form's content, are available, then the following design pattern can be used:

     HeaderOps headerOps = HeaderOps.newInstance();
     headerOps.set("content-type", CTYPE);
     String boundary = headerOps.getFirst("content-type", false)
            .get("boundary");
     FormDataIterator it = new FormDataIterator(is, boundary);
     while (it.hasNext()) {
       InputStream cis = it.next();
       ...
       it.close();
     }
 

Notes:

  • There is an HTML convention that forms with an element whose name is "_charset_" contains the default charset for files whose media type is text/plain. To support this convention, if a the content type is text/plain and a charset parameter is not provided, the default charset is returned by getCharset(). The default charset can be set by calling setDefaultCharset(Charset). to follow this convention, an entry whose name is "_charset_" should have its stream read and then used to set the default charset.
  • If multiple files are being transferred for a single form field, the latest standard, (RFC 7578), requires that all the files use entities with the same name, and the 'filename' parameter can be used to distinguish them. Older software may provide a nested multipart-mixed part. To handle this deprecated usage, the user can simply process the input stream for this form field as an object whose type is multipart/mixed.
  • While this class implements the Iterator interface, there is a restriction on its use: after the method next() has been called, the stream returned must either be read to its end or closed before hasNext() is called. Otherwise an IllegalStateException will be thrown.
  • Constructor Details

    • FormDataIterator

      public FormDataIterator(InputStream is, String boundary) throws IOException
      Constructor.
      Parameters:
      is - an input stream containing the multipart/form-data object
      boundary - the boundary, typically obtained by reading the 'boundary' parameter of a multipart/form-data media type
      Throws:
      IOException - an IO error occurred
  • Method Details

    • setDefaultCharset

      public void setDefaultCharset(Charset charset)
      Set the default character set for this object.
      Parameters:
      charset - the character set; null for the UTF-8 default
    • hasNext

      public boolean hasNext() throws IllegalStateException
      Determine if their are more entries to process.

      Note: if the InputStream returned by next() has not been read fully and if such a stream has not been explicitly closed, an exception will be thrown

      Specified by:
      hasNext in interface Iterator<InputStream>
      Returns:
      true if there are more entries; false otherwise
      Throws:
      IllegalStateException - this method cannot be called when the input stream returned by next() has not been read completely or explicitly closed.
    • next

      public InputStream next()
      Get the next entry. Note: if the InputStream returned by this method has not been read fully and if such a stream has not been explicitly closed, an exception will be thrown when hasNext() is called.
      Specified by:
      next in interface Iterator<InputStream>
      Returns:
      an input stream containing the data for the next entry
    • getHeaders

      public HeaderOps getHeaders()
      Get the headers provided for the current entry. These will typically be a "content-disposition" header and an optional content-type header.
      Returns:
      the headers
    • getName

      public String getName()
      Get the name for the current entry. This will be the same as the name of a control in an HTML form when HTML forms are used. The name is provided in a "content-disposition" header.
      Returns:
      the name; null if there is no header or the name is missing
    • getFileName

      public String getFileName()
      Get the file name for the current entry. When provided, this will typically be the name of a file on a file system accessed by whatever process initiated a POST method. The file name is provided in a "content-disposition" header.
      Returns:
      the name; null if there is no header or the name is missing
    • getContentType

      public String getContentType()
      Get the value for the content-type header for the current entry.
      Returns:
      the full content-type header; null if there is not one
    • getMediaType

      public String getMediaType()
      Get the media type, excluding its parameters, for the current entry
      Returns:
      the media type; null if a media type was not provided
    • getCharset

      public Charset getCharset()
      Get the charset for the current entry
      Returns:
      the charset provided by a "content-type" header; a default charset if there is no content-type header