Getting an InputStream to read more than once, regardless of markSupported()

35,912

Solution 1

You can't necessarily read an InputStream more than once. Some implementations support it, some don't. What you are doing is checking the markSupported method, which is indeed an indicator if you can read the same stream twice, but then you are ignoring the result. You have to call that method to see if you can read the stream twice, and if you can't, make other arrangements.

Edit (in response to comment): When I wrote my answer, my "other arrangements" was to get a fresh InputStream. However, when I read in your comments to your question about what you want to do, I'm not sure it is possible. For the basics of the operation, you probably want RandomAccessFile (at least that would be my first guess, and if it worked, that would be the easiest) - however you will have file access issues. You have an application actively writing to a file, and another reading that file, you will have problems - exactly which problems will depend on the OS, so whatever solution would require more testing. I suggest a separate question on SO that hits on that point, and someone who has tried that out can perhaps give you more insight.

Solution 2

you never mark the stream to be reset

public Clazz(java.io.InputStream defDB)
  {
    firstDBInputStream = defDB.markSupported()?defDB:new BufferedInputStream(defDB);
      //BufferedInputStream supports marking
    firstDBInputStream.mark(500000);//avoid IOException on first reset
  }

public final void loadDatabaseToArrayTable() throws java.io.IOException
  {
    this.dbInputStream = firstDBInputStream;

    dbInputStream.reset();
    dbInputStream.mark(500000);//or however long the data is

    java.util.Scanner fileScanner = new java.util.Scanner(dbInputStream);
    StringBuilder CSV = "";//StringBuilder is more efficient in a loop
    while(fileScanner.hasNextLine())
      CSV.append(fileScanner.nextLine()).append("\n");
    db = ArrayTable.createArrayTableFromCSV(CSV.toString());
  }

however you could instead keep a copy of the original ArrayTable and copy that when you need to (or even the created string to rebuild it)

this code creates the string and caches it so you can safely discard the inputstreams and just use readCSV to build the ArrayTable

  private String readCSV=null;
  public final void loadDatabaseToArrayTable() throws java.io.IOException
  {
    if(readCSV==null){

        this.dbInputStream = firstDBInputStream;


        java.util.Scanner fileScanner = new java.util.Scanner(dbInputStream);
        StringBuilder CSV = "";//StringBuilder is more efficient in a loop
        while(fileScanner.hasNextLine())
          CSV.append(fileScanner.nextLine()).append("\n");

        readCSV=CSV.toString();
        fileScanner.close();

    }
    db = ArrayTable.createArrayTableFromCSV(readCSV);
  }

however if you want new information you'll need to create a new stream to read from again

Share:
35,912
Ky -
Author by

Ky -

Any pronouns. Really, as long as you're respectful, use any singular third-person pronouns to refer to me. More about me on my site! https://KyLeggiero.me Starting on 2016-11-21 and going forward in perpetuity, all content I post on a website in the StackExchange network is written specially for that post, and is licensed under BH-0-PD unless otherwise stated.

Updated on May 26, 2020

Comments

  • Ky -
    Ky - almost 4 years

    I need to be able to re-use a java.io.InputStream multiple times, and I figured the following code would work, but it only works the first time.

    Code


    public class Clazz
    {
      private java.io.InputStream dbInputStream, firstDBInputStream;
      private ArrayTable db;
    
      public Clazz(java.io.InputStream defDB)
      {
        this.firstDBInputStream = defDB;
        this.dbInputStream = defDB;
        if (db == null)
          throw new java.io.FileNotFoundException("Could not find the database at " + db);
        if (dbInputStream.markSupported())
          dbInputStream.mark(Integer.MAX_VALUE);
        loadDatabaseToArrayTable();
      }
    
      public final void loadDatabaseToArrayTable() throws java.io.IOException
      {
        this.dbInputStream = firstDBInputStream;
        if (dbInputStream.markSupported())
          dbInputStream.reset();
    
        java.util.Scanner fileScanner = new java.util.Scanner(dbInputStream);
        String CSV = "";
        for (int i = 0; fileScanner.hasNextLine(); i++)
          CSV += fileScanner.nextLine() + "\n";
        db = ArrayTable.createArrayTableFromCSV(CSV);
      }
    
      public void reloadDatabase()//A method called by the UI
      {
        try
        {
          loadDatabaseToArrayTable();
        }
        catch (Throwable t)
        {
          //Alert the user that an error has occurred
        }
      }
    }
    

    Note that ArrayTable is a class of mine, which uses arrays to give an interface for working with tables.

    Question


    In this program, the database is shown directly to the user immediately after the reloadDatabase() method is called, and so any solution involving saving the initial read to an object in memory is useless, as that will NOT refresh the data (think of it like a browser; when you press "Refresh", you want it to fetch the information again, not just display the information it fetched the first time). How can I read a java.io.InputStream more than once?

  • Ky -
    Ky - over 12 years
    What "Other arrangements" should I make?
  • Ky -
    Ky - over 12 years
    sorry, I forgot to finish copying the whole constructor ^^; firstDBInputStream is marked right after is initialized, and remembering the database won't do me any good. The database stored in the ArrayTable is immediately displayed in the program's main window, and stays that way until it is re-read, so I need to re-read the database whenever the user calls for it to be re-read.
  • Ky -
    Ky - over 12 years
    See my edit, the application writing the [file, etc.] only does so periodically (about once or twice a month), updating like an old webpage would (manually from another user). Is it really that hard to look at the original input stream and create a new one that draws from the same source?
  • ratchet freak
    ratchet freak over 12 years
    @Supuhstar my point stands you can keep the a reference to the fully read in string allowing you to discard the stream as you don't need them anymore. check my edit
  • Ky -
    Ky - over 12 years
    Thanks for clarifying, but it still doesn't help me in this particular case.
  • ratchet freak
    ratchet freak over 12 years
    @Supuhstar just recreate the input stream when you need to read updates. there's no other way
  • Ky -
    Ky - over 12 years
    I think I'll have to find some way to do that. The problem is that it could be any input stream, including java.io.FileInputStream, java.util.jar.JarInputStream, et cetera
  • user390525
    user390525 about 8 years
    @Yishai what if "read more than once" is not supported will that effect the reset() method as well? Comment please