отметка и сброс не работают для bufferedinputStream [duplicate]

У меня была такая же проблема, и я исправил ее с помощью android:contentInset

Попробуйте с этим кодом:

<android.support.design.widget.CoordinatorLayout
    xmlns:app="http://schemas.android.com/apk/res-auto" 
    android:layout_width="match_parent"
    android:layout_height="match_parent">

    <RelativeLayout
        android:layout_width="match_parent"
        android:layout_height="match_parent">

        <android.support.design.widget.AppBarLayout
            android:id="@+id/app_bar_layout"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:fitsSystemWindows="true">

            <android.support.v7.widget.Toolbar
                android:id="@+id/toolbar"
                android:layout_width="match_parent"
                android:layout_height="?attr/actionBarSize"
                android:background="@color/colorPrimary"
                android:contentInsetEnd="50dp"
                android:contentInsetLeft="50dp"
                android:contentInsetRight="50dp"
                android:contentInsetStart="50dp"
                android:theme="@style/ThemeOverlay.AppCompat.Dark.ActionBar"
                app:contentInsetEnd="50dp"
                app:contentInsetLeft="50dp"
                app:contentInsetRight="50dp"
                app:contentInsetStart="50dp"
                app:popupTheme="@style/ThemeOverlay.AppCompat.Light">

                <LinearLayout
                    android:layout_width="match_parent"
                    android:layout_height="wrap_content"
                    android:gravity="center_horizontal"
                    android:layout_centerInParent="true"
                    android:orientation="vertical">

                    <TextView
                        android:layout_width="wrap_content"
                        android:layout_height="wrap_content"
                        android:layout_gravity="center_vertical"
                        android:layout_marginLeft="5dp"
                        android:text="@string/app_name_short"
                        android:textColor="#fff"
                        android:textSize="20dp" />

                </LinearLayout>

            </android.support.v7.widget.Toolbar>

        </android.support.design.widget.AppBarLayout>


        <FrameLayout
            android:id="@+id/main_fragment_container"
            android:layout_width="match_parent"
            android:layout_height="match_parent"
            android:layout_below="@+id/app_bar_layout" />


    </RelativeLayout>

</android.support.design.widget.CoordinatorLayout>
95
задан Warpzit 29 February 2012 в 16:50
поделиться

7 ответов

Вы можете использовать org.apache.commons.io.IOUtils.copy , чтобы скопировать содержимое InputStream в массив байтов, а затем повторно прочитать из массива байтов с помощью ByteArrayInputStream. Например ::

ByteArrayOutputStream baos = new ByteArrayOutputStream();
org.apache.commons.io.IOUtils.copy(in, baos);
byte[] bytes = baos.toByteArray();

// either
while (needToReadAgain) {
    ByteArrayInputStream bais = new ByteArrayInputStream(bytes);
    yourReadMethodHere(bais);
}

// or
ByteArrayInputStream bais = new ByteArrayInputStream(bytes);
while (needToReadAgain) {
    bais.reset();
    yourReadMethodHere(bais);
}
79
ответ дан Captain Man 20 August 2018 в 22:38
поделиться
  • 1
  • 2
    @Paul Grime: IOUtils.toByeArray внутренне вызывает метод копирования изнутри. – Ankit 17 April 2012 в 10:13
  • 3
    Как говорит @Ankit, это решение для меня недействительно, так как вход считывается внутренне и не может быть повторно использован. – Xtreme Biker 29 May 2014 в 15:12
  • 4
    @Extreme, если в вашем случае у вас нет контроля над чтением InputStream (он читается внутренне, как вы упоминаете), тогда вам может быть не повезло. У вас есть доступ к InputStream до «внутреннего» чтения? Я думаю, что пункт @Ankit был другим и касался API. – Paul Grime 29 May 2014 в 15:18
  • 5
    Я знаю, что этот комментарий устарел, но, здесь, в первом варианте, если вы читаете входной поток как массив байтов, не означает ли это, что вы загружаете все данные в память? что может быть большой проблемой, если вы загружаете что-то вроде больших файлов? – jaxkodex 9 January 2015 в 15:23
  • 6

Если вы используете реализацию InputStream , вы можете проверить результат InputStream#markSupported() , который говорит вам, можете ли вы использовать метод mark() / reset() .

Если вы можете пометить поток во время чтения, а затем вызвать reset(), чтобы вернуться к началу.

Если вы не можете, вам придется снова открыть поток.

Другим решением было бы преобразовать InputStream в массив байтов, а затем перебрать по массиву столько раз, сколько вам нужно. Вы можете найти несколько решений в этой статье Преобразование InputStream в байтовый массив в Java с использованием сторонних библиотек или нет. Предупреждение: если прочитанный контент слишком велик, вы можете столкнуться с некоторыми проблемами памяти.

Наконец, если вам нужно прочитать изображение, используйте:

BufferedImage image = ImageIO.read(new URL("http://www.example.com/images/toto.jpg"));

Используя ImageIO#read(java.net.URL) также позволяет использовать кеш.

4
ответ дан alain.janinm 20 August 2018 в 22:38
поделиться
  • 1
    слово предупреждения при использовании ImageIO#read(java.net.URL): некоторые веб-серверы и CDN могут отклонять голые вызовы (т. е. без агента пользователя, который заставляет сервер полагать, что вызов поступает из веб-браузера), сделанный с помощью ImageIO#read. В этом случае, используя URLConnection.openConnection(), устанавливая агент пользователя для этого соединения +, используя `ImageIO.read (InputStream), большую часть времени будет делать трюк. – Clint Eastwood 10 August 2017 в 19:12
  • 2
    InputStream не является интерфейсом – Brice 30 November 2017 в 13:58
  • 3
    @Brice Действительно, спасибо, что указали это! – alain.janinm 30 November 2017 в 16:00

Как насчет:

if (stream.markSupported() == false) {

        // lets replace the stream object
        ByteArrayOutputStream baos = new ByteArrayOutputStream();
        IOUtils.copy(stream, baos);
        stream.close();
        stream = new ByteArrayInputStream(baos.toByteArray());
        // now the stream should support 'mark' and 'reset'

    }
2
ответ дан Anshuman Chatterjee 20 August 2018 в 22:38
поделиться
  • 1
    Это ужасная идея. Вы помещаете все содержимое потока в память. – Niels Doucet 15 June 2018 в 11:33

В зависимости от того, откуда приходит InputStream, вы не сможете его сбросить. Вы можете проверить, поддерживаются ли mark() и reset() с помощью markSupported().

Если это так, вы можете вызвать reset() в InputStream, чтобы вернуться к началу. Если нет, вам нужно снова прочитать InputStream из источника.

20
ответ дан Kevin Parker 20 August 2018 в 22:38
поделиться
  • 1
    InputStream не поддерживает «метку» - вы можете вызывать отметку на IS, но ничего не делает. Аналогично, вызов сброса на IS вызовет исключение. – ayahuasca 5 September 2017 в 13:18

Преобразуйте входной поток в байты, а затем передайте его в функцию savefile, где вы собираете то же самое во входной поток. Также в исходной функции используйте байты для использования для других задач

2
ответ дан Maneesh 20 August 2018 в 22:38
поделиться
  • 1
    Я говорю плохую идею об этом, результирующий массив может быть огромным и уничтожит устройство памяти. – Kevin Parker 9 March 2012 в 22:30

Вы можете обернуть поток ввода с помощью PushbackInputStream. PushbackInputStream позволяет непрочитанным записать назад ») байты, которые уже были прочитаны, поэтому вы можете сделать это следующим образом:

public class StreamTest {
  public static void main(String[] args) throws IOException {
    byte[] bytes = new byte[] { 1, 2, 3, 4, 5, 6, 7, 8, 9 };

    InputStream originalStream = new ByteArrayInputStream(bytes);

    byte[] readBytes = getBytes(originalStream, 3);
    printBytes(readBytes); // prints: 1 2 3

    readBytes = getBytes(originalStream, 3);
    printBytes(readBytes); // prints: 4 5 6

    // now let's wrap it with PushBackInputStream

    originalStream = new ByteArrayInputStream(bytes);

    InputStream wrappedStream = new PushbackInputStream(originalStream, 10); // 10 means that maximnum 10 characters can be "written back" to the stream

    readBytes = getBytes(wrappedStream, 3);
    printBytes(readBytes); // prints 1 2 3

    ((PushbackInputStream) wrappedStream).unread(readBytes, 0, readBytes.length);

    readBytes = getBytes(wrappedStream, 3);
    printBytes(readBytes); // prints 1 2 3


  }

  private static byte[] getBytes(InputStream is, int howManyBytes) throws IOException {
    System.out.print("Reading stream: ");

    byte[] buf = new byte[howManyBytes];

    int next = 0;
    for (int i = 0; i < howManyBytes; i++) {
      next = is.read();
      if (next > 0) {
        buf[i] = (byte) next;
      }
    }
    return buf;
  }

  private static void printBytes(byte[] buffer) throws IOException {
    System.out.print("Reading stream: ");

    for (int i = 0; i < buffer.length; i++) {
      System.out.print(buffer[i] + " ");
    }
    System.out.println();
  }


}

Обратите внимание, что PushbackInputStream хранит внутренний буфер байтов, поэтому он действительно создает буфер в памяти, который содержит байты «записаны».

Зная этот подход, мы можем пойти дальше и объединить его с FilterInputStream. FilterInputStream хранит исходный входной поток в качестве делегата. Это позволяет создать новое определение класса, которое автоматически позволяет « непрочитанных » исходных данных. Определение этого класса следующее:

public class TryReadInputStream extends FilterInputStream {
  private final int maxPushbackBufferSize;

  /**
  * Creates a <code>FilterInputStream</code>
  * by assigning the  argument <code>in</code>
  * to the field <code>this.in</code> so as
  * to remember it for later use.
  *
  * @param in the underlying input stream, or <code>null</code> if
  *           this instance is to be created without an underlying stream.
  */
  public TryReadInputStream(InputStream in, int maxPushbackBufferSize) {
    super(new PushbackInputStream(in, maxPushbackBufferSize));
    this.maxPushbackBufferSize = maxPushbackBufferSize;
  }

  /**
   * Reads from input stream the <code>length</code> of bytes to given buffer. The read bytes are still avilable
   * in the stream
   *
   * @param buffer the destination buffer to which read the data
   * @param offset  the start offset in the destination <code>buffer</code>
   * @aram length how many bytes to read from the stream to buff. Length needs to be less than
   *        <code>maxPushbackBufferSize</code> or IOException will be thrown
   *
   * @return number of bytes read
   * @throws java.io.IOException in case length is
   */
  public int tryRead(byte[] buffer, int offset, int length) throws IOException {
    validateMaxLength(length);

    // NOTE: below reading byte by byte instead of "int bytesRead = is.read(firstBytes, 0, maxBytesOfResponseToLog);"
    // because read() guarantees to read a byte

    int bytesRead = 0;

    int nextByte = 0;

    for (int i = 0; (i < length) && (nextByte >= 0); i++) {
      nextByte = read();
      if (nextByte >= 0) {
        buffer[offset + bytesRead++] = (byte) nextByte;
      }
    }

    if (bytesRead > 0) {
      ((PushbackInputStream) in).unread(buffer, offset, bytesRead);
    }

    return bytesRead;

  }

  public byte[] tryRead(int maxBytesToRead) throws IOException {
    validateMaxLength(maxBytesToRead);

    ByteArrayOutputStream baos = new ByteArrayOutputStream(); // as ByteArrayOutputStream to dynamically allocate internal bytes array instead of allocating possibly large buffer (if maxBytesToRead is large)

    // NOTE: below reading byte by byte instead of "int bytesRead = is.read(firstBytes, 0, maxBytesOfResponseToLog);"
    // because read() guarantees to read a byte

    int nextByte = 0;

    for (int i = 0; (i < maxBytesToRead) && (nextByte >= 0); i++) {
      nextByte = read();
      if (nextByte >= 0) {
        baos.write((byte) nextByte);
      }
    }

    byte[] buffer = baos.toByteArray();

    if (buffer.length > 0) {
      ((PushbackInputStream) in).unread(buffer, 0, buffer.length);
    }

    return buffer;

  }

  private void validateMaxLength(int length) throws IOException {
    if (length > maxPushbackBufferSize) {
      throw new IOException(
        "Trying to read more bytes than maxBytesToRead. Max bytes: " + maxPushbackBufferSize + ". Trying to read: " +
        length);
    }
  }

}

Этот класс имеет два метода. Один для чтения в существующий буфер (defintion аналогичен вызову public int read(byte b[], int off, int len) класса InputStream). Второй, который возвращает новый буфер (это может быть более эффективным, если размер буфера для чтения неизвестен).

Теперь посмотрим наш класс в действии:

public class StreamTest2 {
  public static void main(String[] args) throws IOException {
    byte[] bytes = new byte[] { 1, 2, 3, 4, 5, 6, 7, 8, 9 };

    InputStream originalStream = new ByteArrayInputStream(bytes);

    byte[] readBytes = getBytes(originalStream, 3);
    printBytes(readBytes); // prints: 1 2 3

    readBytes = getBytes(originalStream, 3);
    printBytes(readBytes); // prints: 4 5 6

    // now let's use our TryReadInputStream

    originalStream = new ByteArrayInputStream(bytes);

    InputStream wrappedStream = new TryReadInputStream(originalStream, 10);

    readBytes = ((TryReadInputStream) wrappedStream).tryRead(3); // NOTE: no manual call to "unread"(!) because TryReadInputStream handles this internally
    printBytes(readBytes); // prints 1 2 3

    readBytes = ((TryReadInputStream) wrappedStream).tryRead(3); 
    printBytes(readBytes); // prints 1 2 3

    readBytes = ((TryReadInputStream) wrappedStream).tryRead(3);
    printBytes(readBytes); // prints 1 2 3

    // we can also call normal read which will actually read the bytes without "writing them back"
    readBytes = getBytes(wrappedStream, 3);
    printBytes(readBytes); // prints 1 2 3

    readBytes = getBytes(wrappedStream, 3);
    printBytes(readBytes); // prints 4 5 6

    readBytes = ((TryReadInputStream) wrappedStream).tryRead(3); // now we can try read next bytes
    printBytes(readBytes); // prints 7 8 9

    readBytes = ((TryReadInputStream) wrappedStream).tryRead(3); 
    printBytes(readBytes); // prints 7 8 9


  }



}
8
ответ дан walkeros 20 August 2018 в 22:38
поделиться

, если ваша InputStream поддерживает использование метки, вы можете mark() использовать ваш входной поток, а затем reset(). если ваш InputStrem не поддерживает отметку, вы можете использовать класс java.io.BufferedInputStream, чтобы вы могли встраивать свой поток внутри BufferedInputStream, например

    InputStream bufferdInputStream = new BufferedInputStream(yourInputStream);
    bufferdInputStream.mark(some_value);
    //read your bufferdInputStream 
    bufferdInputStream.reset();
    //read it again
6
ответ дан wannas 20 August 2018 в 22:38
поделиться
  • 1
    Буферизованный входной поток может только отбрасывать размер буфера, поэтому, если источник не подходит, вы не можете вернуться к началу. – L. Blanc 6 June 2018 в 15:32
Другие вопросы по тегам:

Похожие вопросы: