java - Android:如何从InputStream中随机访问?

标签 java android random-access bufferedinputstream randomaccessfile

我有一个InputStream,以及相对文件名和大小。

我需要访问/读取输入流中的一些随机(增加)位置。该位置存储在整数数组中(称为偏移量)。

InputStream inputStream = ...

String fileName = ...
int fileSize = (int) ...

int[] offsets = new int[]{...};  // the random (increasing) offsets array

现在,给定一个输入流,我发现只有两种可能的解决方案可以跳转到文件的随机(增加)位置。

第一个是使用 skip() InputStream的方法(请注意,我实际上使用 BufferedInputStream ,因为我需要 mark()reset() 文件指针)。

//Open a BufferInputStream:
BufferedInputStream bufferedInputStream = new BufferedInputStream(inputStream);

byte[] bytes = new byte[1];

int curFilePointer = 0;
long numBytesSkipped = 0;
long numBytesToSkip = 0;
int numBytesRead = 0;

//Check the file size:
if ( fileSize < offsets[offsets.length-1] ) {  // the last (bigger) offset is bigger then the file size... 
    //Debug:
    Log.d(TAG, "The file is too small!\n");

    return;
}

for (int i=0, k=0; i < offsets.length; i++, k=0) {  // for each offset I have to jump...
    try {
        //Jump to the offset [i]:
        while( (curFilePointer < offsets[i]) && (k < 10) ) {  // until the correct offset is reached (at most 10 tries)
            numBytesToSkip = offsets[i] - curFilePointer;
            numBytesSkipped = bufferedInputStream.skip(numBytesToSkip);

            curFilePointer += numBytesSkipped;  // move the file pointer forward

            //Debug:
            Log.d(TAG, "FP: " + curFilePointer + "\n");

            k++;
        }

        if ( curFilePointer != offsets[i] ) {  // it did NOT jump properly... (what's going on?!)
            //Debug:
            Log.d(TAG, "InputStream.skip() DID NOT JUMP PROPERLY!!!\n");

            break;
        }

        //Read the content of the file at the offset [i]:
        numBytesRead = bufferedInputStream.read(bytes, 0, bytes.length);
        curFilePointer += numBytesRead;  // move the file pointer forward

        //Debug:
        Log.d(TAG, "READ [" + curFilePointer + "]: " + bytes[0] + "\n");
    }
    catch ( IOException e ) {
        e.printStackTrace();

        break;
    }
    catch ( IndexOutOfBoundsException e ) {
        e.printStackTrace();

        break;
    }
}

//Close the BufferInputStream:
bufferedInputStream.close() 

问题是,在我的测试期间,对于某些(通常很大)偏移量,它在跳过正确的字节数之前已经循环了 5 次或更多次。正常吗?最重要的是,我可以/应该推skip()吗? (也就是说:10 个周期是否足以确保它始终到达正确的偏移量?)

我发现的唯一替代方法是创建 RandomAccessFileInputStream,通过 File.createTempFile(prefix, suffix, directory)以及以下函数。

public static RandomAccessFile toRandomAccessFile(InputStream inputStream, File tempFile, int fileSize) throws IOException {
    RandomAccessFile randomAccessFile = new RandomAccessFile(tempFile, "rw");

    byte[] buffer = new byte[fileSize];
    int numBytesRead = 0;

    while ( (numBytesRead = inputStream.read(buffer)) != -1 ) {
        randomAccessFile.write(buffer, 0, numBytesRead);
    }

    randomAccessFile.seek(0);

    return randomAccessFile;
}

拥有RandomAccessFile实际上是一个更好的解决方案,但性能却呈指数级恶化(最重要的是因为我将拥有多个文件)。

<小时/>

编辑:使用byte[] buffer = new byte[fileSize]可以加快(并且很多)RandomAccessFile的创建速度!

<小时/>
//Create a temporary RandomAccessFile:
File tempFile = File.createTempFile(fileName, null, context.getCacheDir());
RandomAccessFile randomAccessFile = toRandomAccessFile(inputStream, tempFile, fileSize);

byte[] bytes = new byte[1];
int numBytesRead = 0;

//Check the file size:
if ( fileSize < offsets[offsets.length-1] ) {  // the last (bigger) offset is bigger then the file size...
    //Debug:
    Log.d(TAG, "The file is too small!\n");

    return;
}

for (int i=0, k=0; i < offsets.length; i++, k=0) {  // for each offset I have to jump...
    try {
        //Jump to the offset [i]:
        randomAccessFile.seek(offsets[i]);

        //Read the content of the file at the offset [i]:
        numBytesRead = randomAccessFile.read(bytes, 0, bytes.length);

        //Debug:
        Log.d(TAG, "READ [" + (randomAccessFile.getFilePointer()-4) + "]: " + bytes[0] + "\n");
    }
    catch ( IOException e ) {
        e.printStackTrace();

        break;
    }
    catch ( IndexOutOfBoundsException e ) {
        e.printStackTrace();

        break;
    }
}

//Delete the temporary RandomAccessFile:
randomAccessFile.close();
tempFile.delete();

现在,是否有更好(或更优雅)的解决方案来从 InputStream 进行“随机”访问?

最佳答案

有点不幸的是,您一开始就有一个InputStream,但在这种情况下,如果您总是向前跳过,则在文件中缓冲流是没有用的。但您不必计算调用 skip 的次数,这并不是真正令人感兴趣的。

要做必须检查流是否已经结束,以防止无限循环。检查source对于默认的 skip 实现,我想说你必须继续调用 skip 直到它返回 0。这将表明已到达流末尾。根据我的口味,JavaDoc 对此有点不清楚。

关于java - Android:如何从InputStream中随机访问?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/19276817/

相关文章:

java - 为什么当我将元素从一维数组传输到二维数组时,在获取上一个循环生成的最后一个一维数组时

java - 如何导入和使用zxing库?

java - Groovy 使方法从一个脚本到另一个脚本可见

java - 是否可以将随机访问文件中的特定字节设置回空?

java - 将随机访问文件透明写入 zip 文件

java - 在运行时用 Java 编译 Groovy 类

java - 尝试从 Google 相册应用中选择照片时出现 IllegalStateException

android - 真正实时流式传输到 Android/iPhone

android - Mifare 超轻 : lock specific pages

java - 使用 RandomAccessFile 通过 Internet 下载文件