Actionscript 3,只能读取文件的一部分而不将整个文件加载到内存中

Actionscript 3, can only part of a file be read without loading the whole file into memory

我正在尝试在 Air 桌面应用程序中加载 600mb 文件的 10mb 块。起初我尝试加载整个文件,但出现 "system out of memory" 错误,或者 SWF 会直接退出而不会出现错误。

我还没有移动到只将文件的一部分读入字节数组,清除数组然后读取下一部分。这样效果更好,但它仍然会在文件末尾出现内存问题。

据我了解,以下方法应该只读取请求的文件部分。但我认为出于某种原因,我想要的块从头到尾的文件实际上被读取了。我的缩减代码如下所示:

var file:File = File.desktopDirectory.resolvePath('input.txt');
var fileStream:FileStream = new FileStream();
fileStream.addEventListener(Event.COMPLETE, onLoaded);
fileStream.openAsync(file, FileMode.READ);  
fileStream.position = _currentPos;

加载中:

var fileStream:FileStream = event.target as FileStream;
_fileSize = fileStream.bytesAvailable;
bytes.clear();
fileStream.readBytes(bytes, 0, 10000000); 

然后我对字节做一些事情并再次调用上面的代码,这次位置是前一个位置加上前一个读取的长度。

当我检查字节数组时,我可以看到大小等于文件开头到读取结尾的大小。字节数组在读取开始之前的元素中没有数据,这是我所期望的。

那么我是只读取我想要的块还是整个文件?

如果我不是,我可以这样做吗?

如何解决内存问题?

我得到的错误是

Error: Error #1000: The system is out of memory.
at flash.filesystem::FileStream/readBytes()

编辑:

经过更多测试后,我确信整个文件都被读入了内存。

System.privateMemory 报告内存使用量刚好超过文件大小,当我检查 FileStream 时,我看到的是文件本身的大小,而不是我想要的块。

编辑 2:我现在添加了 readAhead 和 progress 事件,但它实际上以相同的方式运行,除了我控制预读大小。 readAhead 实际上会阻止文件从文件开始到读取位置的开始读取吗?

好像fileStream.position什么都没做。

我的新代码如下所示:

    private function read(pos:int):void {
    _bytes.clear();

    var file:File = File.desktopDirectory.resolvePath('big.mp4');
    var fileStream:FileStream = new FileStream();
    fileStream.readAhead = 10000000;
    fileStream.addEventListener(ProgressEvent.PROGRESS, readProgressHandler)
    fileStream.openAsync(file, FileMode.READ);  
    fileStream.position = _pos;

        function readProgressHandler(event:ProgressEvent):void {
            if (fileStream.bytesAvailable >= 10000000) {
                fileStream.removeEventListener(ProgressEvent.PROGRESS, readProgressHandler) 
                fileStream.readBytes(_bytes, _pos, 10000000);
                fileStream.close();

                //get next bit
                go();
            }
        }

    }

    private function go():void {
        _pos = _pos + 10000000;
        read(_pos);
    }

如果你想异步读取文件而不想加载整个文件你应该设置readAhead 属性。

The default value of this property is infinity: by default a file that is opened to read asynchronously reads as far as the end of the file.

另请注意,COMPLETE 事件是在读取整个文件时调度的,因此您可能想使用 PROGRESS 事件来代替

Signals the availability of new data on the stream.

并且不要将 FileStream 视为 ByteArray 的一种,因为它不是。它使用相同的数据 input/ouput 接口,但您从中读取的数据并未存储在 RAM 中 - 您只需从硬盘读取数据即可。 ByteArray 例如 length 属性 不是文件流的 属性。

您还会发现 This demonstration 对您有用。

编辑

异步读取的全部目的是在每次调用进度事件侦听器时获取文件的一部分,这样您就不会一遍又一遍地打开和关闭文件。下面的示例从文件中读取 3MB。您可以从任何位置开始阅读。

var _pos:Number = 100000; //starting positon - could be any within a file.
var bytes:ByteArray = new ByteArray();
var totalToRead:Number = 3 * 1024 * 1024;
var file:File = File.desktopDirectory.resolvePath("SILENCE GROOVE - live at Kompas Audio .mp3");
var fileStream:FileStream = new FileStream();
fileStream.readAhead = 10000000;
fileStream.addEventListener(ProgressEvent.PROGRESS, readProgressHandler);
fileStream.openAsync(file, FileMode.READ);
fileStream.position = _pos;

function readProgressHandler(event:ProgressEvent):void {
    var tr:Number = Math.min(totalToRead - bytes.length , fileStream.bytesAvailable); //bytes to read.
    fileStream.readBytes(bytes, bytes.length, tr);
    if (bytes.length < totalToRead) return;
    fileStream.removeEventListener(ProgressEvent.PROGRESS, readProgressHandler);
    fileStream.close();
    trace(bytes.length / (1024 * 1024), "MB readed.");
}

更多详情:

并确保一切都清楚。您不需要采取任何额外的步骤将其拆分为 10MB 的块。您可以将 totalToRead 设置为 700MB 并随时读取文件数据(一旦可用)。 readAhead 属性 只是告诉应该将多少字节加载到内存中,即使您不立即读取它们也是如此。您可能会在每个 ProgressEvent 处获得较少的数据,但事件将是调度程序,直到您达到 readAhead 值,然后文件读取已售出停止单元读取已加载的数据。