Read file at a certain rate in Java

前端 未结 6 574
梦谈多话
梦谈多话 2021-01-05 10:44

Is there an article/algorithm on how I can read a long file at a certain rate?

Say I do not want to pass 10 KB/sec while issuing reads.

6条回答
  •  不思量自难忘°
    2021-01-05 11:30

    A simple solution, by creating a ThrottledInputStream.

    This should be used like this:

            final InputStream slowIS = new ThrottledInputStream(new BufferedInputStream(new FileInputStream("c:\\file.txt"),8000),300);
    

    300 is the number of kilobytes per second. 8000 is the block size for BufferedInputStream.

    This should of course be generalized by implementing read(byte b[], int off, int len), which will spare you a ton of System.currentTimeMillis() calls. System.currentTimeMillis() is called once for each byte read, which can cause a bit of an overhead. It should also be possible to store the number of bytes that can savely be read without calling System.currentTimeMillis().

    Be sure to put a BufferedInputStream in between, otherwise the FileInputStream will be polled in single bytes rather than blocks. This will reduce the CPU load form 10% to almost 0. You will risk to exceed the data rate by the number of bytes in the block size.

    import java.io.InputStream;
    import java.io.IOException;
    
    public class ThrottledInputStream extends InputStream {
        private final InputStream rawStream;
        private long totalBytesRead;
        private long startTimeMillis;
    
        private static final int BYTES_PER_KILOBYTE = 1024;
        private static final int MILLIS_PER_SECOND = 1000;
        private final int ratePerMillis;
    
        public ThrottledInputStream(InputStream rawStream, int kBytesPersecond) {
            this.rawStream = rawStream;
            ratePerMillis = kBytesPersecond * BYTES_PER_KILOBYTE / MILLIS_PER_SECOND;
        }
    
        @Override
        public int read() throws IOException {
            if (startTimeMillis == 0) {
                startTimeMillis = System.currentTimeMillis();
            }
            long now = System.currentTimeMillis();
            long interval = now - startTimeMillis;
            //see if we are too fast..
            if (interval * ratePerMillis < totalBytesRead + 1) { //+1 because we are reading 1 byte
                try {
                    final long sleepTime = ratePerMillis / (totalBytesRead + 1) - interval; // will most likely only be relevant on the first few passes
                    Thread.sleep(Math.max(1, sleepTime));
                } catch (InterruptedException e) {//never realized what that is good for :)
                }
            }
            totalBytesRead += 1;
            return rawStream.read();
        }
    }
    

提交回复
热议问题