Talking about the problem that request. getinputstream can only be read once

  • 2021-12-05 07:00:33
  • OfStack

First, we review the basic knowledge of InputStream read method under 1.

There is one inside java InputStream read method, postion, which marks the position read by the current stream. Every time it is read, the position will move once. If it is read to the end, InputStream. read method will return-1, which marks that it has been read. If you want to read again, you can call inputstream. reset method, and position will move to the position where mark was last called. mark defaults to 0, so you can read it from scratch.

Of course, reset is conditional, depending on whether markSupported, markSupported () methods return mark/reset

Let's look back at request. getInputStream

The value returned by request. getInputStream is ServletInputStream. Looking at ServletInputStream source code, we found that reset method was not overridden, so looking at InputStream source code found that marksupported returned false, and reset method directly threw an exception.

InputStream.java


 public boolean markSupported() {
    return false;
  }

 public synchronized void reset() throws IOException {
    throw new IOException("mark/reset not supported");
  }

To sum up, position reaches the end of the file after request. getinputstream is read once, and the data cannot be read the second time. Because reset () cannot be read, request. getinputstream can only be read once.

Summary:

The most fundamental problem is the deep understanding of read and reset methods of java and IO, especially the internal implementation principle of read method.

Attached ServletInputStream. java source code


/*
 * Licensed to the Apache Software Foundation (ASF) under one or more
 * contributor license agreements. See the NOTICE file distributed with
 * this work for additional information regarding copyright ownership.
 * The ASF licenses this file to You under the Apache License, Version 2.0
 * (the "License"); you may not use this file except in compliance with
 * the License. You may obtain a copy of the License at
 *
 *   http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package javax.servlet;

import java.io.IOException;
import java.io.InputStream;

/**
 * Provides an input stream for reading binary data from a client request,
 * including an efficient <code>readLine</code> method for reading data one line
 * at a time. With some protocols, such as HTTP POST and PUT, a
 * <code>ServletInputStream</code> object can be used to read data sent from the
 * client.
 * <p>
 * A <code>ServletInputStream</code> object is normally retrieved via the
 * {@link ServletRequest#getInputStream} method.
 * <p>
 * This is an abstract class that a servlet container implements. Subclasses of
 * this class must implement the <code>java.io.InputStream.read()</code> method.
 *
 * @see ServletRequest
 */
public abstract class ServletInputStream extends InputStream {

  /**
   * Does nothing, because this is an abstract class.
   */
  protected ServletInputStream() {
    // NOOP
  }

  /**
   * Reads the input stream, one line at a time. Starting at an offset, reads
   * bytes into an array, until it reads a certain number of bytes or reaches
   * a newline character, which it reads into the array as well.
   * <p>
   * This method returns -1 if it reaches the end of the input stream before
   * reading the maximum number of bytes.
   *
   * @param b
   *      an array of bytes into which data is read
   * @param off
   *      an integer specifying the character at which this method
   *      begins reading
   * @param len
   *      an integer specifying the maximum number of bytes to read
   * @return an integer specifying the actual number of bytes read, or -1 if
   *     the end of the stream is reached
   * @exception IOException
   *        if an input or output exception has occurred
   */
  public int readLine(byte[] b, int off, int len) throws IOException {

    if (len <= 0) {
      return 0;
    }
    int count = 0, c;

    while ((c = read()) != -1) {
      b[off++] = (byte) c;
      count++;
      if (c == '\n' || count == len) {
        break;
      }
    }
    return count > 0 ? count : -1;
  }

  /**
   * Returns <code>true</code> if all the data has been read from the stream,
   * else <code>false</code>.
   *
   * @since Servlet 3.1
   */
  public abstract boolean isFinished();

  /**
   * Returns <code>true</code> if data can be read without blocking, else
   * <code>false</code>. If this method is called and returns false, the
   * container will invoke {@link ReadListener#onDataAvailable()} when data is
   * available.
   *
   * @since Servlet 3.1
   */
  public abstract boolean isReady();

  /**
   * Sets the {@link ReadListener} for this {@link ServletInputStream} and
   * thereby switches to non-blocking IO. It is only valid to switch to
   * non-blocking IO within async processing or HTTP upgrade processing.
   *
   * @param listener The non-blocking IO read listener
   *
   * @throws IllegalStateException  If this method is called if neither
   *                 async nor HTTP upgrade is in progress or
   *                 if the {@link ReadListener} has already
   *                 been set
   * @throws NullPointerException   If listener is null
   *
   * @since Servlet 3.1
   */
  public abstract void setReadListener(ReadListener listener);
}


Related articles: